《高阶Perl》——3.4 记忆术

3.4 记忆术

给函数添加缓存技术代码不是非常困难的。且已经看到,需要的改变对任何函数几乎一样。那么,为什么不让计算机做这些呢?若告诉Perl想要使一个函数具有缓存行为。Perl应该能自动地执行所需的转换。这样的给函数添加缓存行为的自动的转换就称为记忆术(memoization),函数则称为带记忆的(memoized)。

标准的Memoize模块就是做这个的。如果Memoize模块可用,就完全不必重写fib代码。可以简单地在程序的顶部添加两行:

### Code Library: fib-automemo
use Memoize;
memoize 'fib';
# Compute the number of pairs of rabbits alive in month n
sub fib {
  my ($month) = @_;
  if ($month < 2) { return $month }
  fib($month-2) + fib($month-1);
}

fib现在展现了缓存行为。代码和原先的慢版完全一样,但是函数不再慢了。

时间: 2024-07-31 09:20:28

《高阶Perl》——3.4 记忆术的相关文章

《高阶Perl》——3.10 可供选择的记忆术

3.10 可供选择的记忆术 大多数纯函数提供一个缓存的机会.尽管乍一看纯函数很少,它们只以一定频率出现.纯函数特别普遍的地方是在排序中用做比较器函数. Perl内置的sort操作符是通用的,它可以把一列任何种类的数据以程序要求的任何次序排序.默认状态下,它把一列字符串以字母表次序排序,但是程序员可以任意提供一个比较器函数(comparator function),告诉Perl怎样重排sort的参数列表.比较器函数被反复调用,每次带有待排序列表中的两个不同元素,如果两个元素次序正确,就必须返回一个

《高阶Perl》——3.5 MEMOIZE模块

3.5 MEMOIZE模块 本书不是关于Perl模块的内部细节的,而是关于一些Memoize模块内部使用的并和稍后要做的事情有直接联系的技术,所以现在简短地介绍一下. Memoize得到一个函数名(或引用)作为它的参数.它制造一个新的函数,后者维护一个缓存并在其中查找它的参数.如果新的函数在缓存里找到了参数,就返回缓存的值:如果没找到,就调用原始函数,把返回的值保存入缓存,并把它返回给原始的主调者. 制造完这个新的函数,Memoize就把它装入Perl的符号表以代替原始的函数,那样当你认为你在调

《高阶Perl》——3.7 键的生成

3.7 键的生成 先前的记忆器至少有一个严重的问题.它需要把函数的参数转变成一个散列键,它的做法是使用join: my $key = join ',', @_; 这对只带一个参数的函数有效,也对参数不含逗号的函数有效,包括所有的参数是数字的函数.但是如果函数的参数可能包含逗号,它可能失效,因为以下两个调用却计算出了相同的键: func("x,", "y"); func("x", ",y"); 第一次调用,返回的值将以键&quo

《高阶Perl》——3.12 速度的好处

3.12 速度的好处 此刻有个说法比较诱人,那就是记忆术只比手动缓存技术改善了一点,因为它做了同样的事情,仅有的额外的好处是你可以快速启动或者关闭它.但这不完全正确.当工具之间的速度和便利的差别够大,它会改变你思考和使用工具的方式.自动地使一个函数带记忆仅需要消耗手动书写代码的1/100的时间.这和飞机与牛车的速度差别一样.说飞机只是更快的牛车就忽略了本质:量变如此巨大以致它也变成了实质的质变. 例如,有了自动的记忆术,就有可能为函数增加缓存行为而不必预先仔细考虑性能细节.记忆术这么简单以至于可

《高阶Perl》——3.11 传播福音

3.11 传播福音 如果你正尝试向一个C程序员解释为什么Perl好,自动的记忆术是一个好例子.几乎所有的程序员都熟悉缓存技术.即使他们没在自己的程序里使用过任何缓存技术,他们也一定熟悉这个概念,来自网页浏览器里的,他们的计算机的缓存内存里的,DNS服务器里的,他们的网页代理服务器里的,或别的地方的缓存.缓存,就像大多数简单实用的主意,是无处不在的. 增加缓存不是非常麻烦,但至少需要几分钟修改代码.算上所有修改,你有可能犯错,这不得不计算进平均时间,一旦你完成了,可能发现缓存是个糟糕的主意,因为缓

《高阶Perl》——3.9 持续的缓存

3.9 持续的缓存 在离开自动的记忆术的主题之前,将浏览一些外围的技术.将看到一个函数如何被带记忆的版本替代,后者在缓存里存储了返回值,缓存就仅是一个散列变量. 在Perl里,可以使用tie操作符把一个散列变量关联到一个磁盘上的数据库,那么存储在散列的数据会自动写到磁盘上,从散列取回数据实际上是从磁盘取回.把这个功能增加到memoize函数是简单的: use DB_File; sub memoize { my ($func, $keygen, $file) = @_; my %cache; if

《高阶Perl》——3.6 CAVEATS

3.6 CAVEATS (这是拉丁文的"警告".) 显然,记忆术不适合所有的性能问题.它甚至不适合所有的函数.有几类函数就不应该带记忆. 3.6.1 返回值不依赖参数的函数 记忆术最适合那些返回值只依赖它们的参数的函数.想象一下使时间函数带记忆的愚蠢:第一次你调用它,你将得到时刻,随后的调用将会返回一样的时刻.类似地,想象一个带记忆的随机数生成器是多么固执. 或者想象一个返回值是指示某类成功或失败的函数.你不会希望这类函数是带记忆的,每次被调用都返回同一个值. 然而,记忆术适合一些这样

《高阶Perl》——导读

前 言 在编程圈子里有一句著名的俗语,一个优秀的Fortran程序员可以用任何语言写Fortran程序.然而,让人悲哀的是,不管他们是否愿意,Fortran程序员用任何语言写Fortran程序.类似地,作为Perl程序员,我们也在用Perl写C程序,不管我们是否愿意.这让人羞愧,因为Perl是一门比C更富有表现力的语言.我们本可以做得更好,以C程序员梦想不到的方式使用Perl,但是我们没有那样做. 怎么会这样呢?Perl的设计初衷是一方面作为C的替代品,另一方面作为UNIX脚本语言(如Bourn

《高阶Perl》——3.2 内联缓存

3.2 内联缓存 给一个函数添加缓存的最直接的方式就是给函数一个私有的散列.在这个例子里,可以使用一个数组代替散列,因为fib()的参数总是一个非负整数.但是一般需要使用一个散列,那么将会看到: ### Code Library: fib-cached # Compute the number of pairs of rabbits alive in month n { my %cache; sub fib { my ($month) = @_; unless (exists $cache{$m