我正在阅读https://www.packtpub.com/application-development/haskell-high-performance-programming这本书,试图弄清楚这两个功能之间的区别是什么:
此功能确实会记住中间数字:
fib_mem :: Int -> Integer
fib_mem = (map fib [0..] !!)
where fib 0 = 1
fib 1 = 1
fib n = fib_mem (n-2) + fib_mem (n-1)
而不是:
fib_mem_arg :: Int -> Integer
fib_mem_arg x = map fib [0..] !! x
where fib 0 = 1
fib 1 = 1
fib n = fib_mem_arg (n-2) + fib_mem_arg (n-1)
作者尝试解释如下:
运行fib_mem_arg时除了很小的参数外,都可以 确认它没有备注。即使我们可以看到那个地图 [0 ..]并不取决于参数编号,可以记住, 不会,因为将参数应用于函数将创建 一个新的表达式,它不能隐式地具有指向表达式的指针 来自以前的功能应用程序。
用粗体标记的句子是什么意思?有人可以给我一个简单的例子吗?
为什么fib_mem
是constant applicative form
?
答案 0 :(得分:3)
为什么
fib_mem
是恒定的适用形式?
不是fib_mem
,而是(map fib [0..] !!)
。它是CAF,因为它是部分应用的功能(!!)
。因此,它会保留内存。
(另请参见:What are super combinators and constant applicative forms?)
由于类型是单态的,因此即使在调用fib_mem
之间也将其保留在内存中,实际上就像map fib [0..]
被“浮动”到顶层一样,就像定义为
fib_mem_m :: Int -> Integer
fib_mem_m = (the_list !!)
where fib 0 = 1
fib 1 = 1
fib n = (the_list !! (n-2)) + (the_list !! (n-1))
the_list = map fib [0..]
如果类型是多态的,则不可能浮动到顶层,但在每次调用fib_mem
的过程中仍会保留,就像定义为
fib_mem_p :: Num a => Int -> a
fib_mem_p = (the_list !!)
where fib 0 = 1
fib 1 = 1
fib n = (the_list !! (n-2)) + (the_list !! (n-1))
the_list = map fib [0..]
要查看差异,请在GHCi属性下对fib_mem_m 10000
进行两次评估。第二次尝试将花费0秒。但是fib_mem_p 10000
每次调用将花费相同的时间。它仍然会像第一个一样快,因此仍然存在备忘录,只是在两次调用之间没有保留。
采用这种定义方式,fib_mem_arg
中的完整应用程序也会被记住-就像上面的那样,不在两次fib_mem_arg
的调用之间,而仅在每次调用期间。 / p>
fib_mem_arg :: Num a => Int -> Integer -- or polymorphic, makes no difference
fib_mem_arg x = the_list !! x
where fib 0 = 1
fib 1 = 1
fib n = (the_list !! (n-2)) + (the_list !! (n-1))
the_list = map fib [0..]