Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 斐波那契序列生成_Haskell_Fibonacci - Fatal编程技术网

Haskell 斐波那契序列生成

Haskell 斐波那契序列生成,haskell,fibonacci,Haskell,Fibonacci,我正在写一个斐波那契序列生成器,我试图理解Haskell中的以下代码 fibs=1:1:zipWith(+)fibs(尾部fibs) 我知道zipWith是什么,但我不知道程序是如何执行的,为什么它会生成所有斐波那契数。我试图理解为什么它不会终止在函数式语言中使用环境概念,如下所示: 最初,由于Haskell的惰性求值,env中的绑定应该是fibs:[1,1,x],然后为了求值fibs,解释器求值x,在这种情况下,它是带(+)fibs(尾fibs)的zipWith。当使用计算zipWith时,

我正在写一个斐波那契序列生成器,我试图理解Haskell中的以下代码

fibs=1:1:zipWith(+)fibs(尾部fibs)

我知道zipWith是什么,但我不知道程序是如何执行的,为什么它会生成所有斐波那契数。我试图理解为什么它不会终止在函数式语言中使用环境概念,如下所示:

最初,由于Haskell的惰性求值,
env
中的绑定应该是
fibs:[1,1,x]
,然后为了求值
fibs
,解释器求值
x
,在这种情况下,它是带(+)fibs(尾fibs)的zipWith。当使用计算
zipWith时,它会得到
fibs:[1,1,2,x]
,这也是因为Haskell的延迟计算。此时,
env
中的
fibs
绑定到
[1,1,2,x]
。但是,要完全评估fibs,它将继续评估x,我们将返回到前面的步骤

这是正确的吗


此外,我注意到,当我在
ghci
中运行上述程序时,它会立即提示当前已计算的斐波那契序列,为什么?它不应该在完成所有计算后打印结果吗?

因此,您的大部分推理都是正确的。特别是,您正确地描述了如何根据旧元素评估列表中的每个新元素。同样正确的是,要完全评估
fibs
需要重复您概述的步骤,事实上,这将永远循环

您缺少的关键因素是我们不必对列表进行全面评估。像
fibs=…
这样的绑定只为表达式指定一个名称;它不需要评估整个列表。Haskell只计算运行
main
所需的列表数量。例如,如果我们的
main

main = print $ fibs !! 100
Haskell将只计算
fibs
(按照您概述的步骤)的前100个元素,但不需要更多元素,也不会永远循环

此外,即使我们正在评估整个事情(这将永远循环),我们也可以使用我们计算的部分。这正是当您在ghci中看到
fibs
的值时所发生的情况:它在计算每个元素时尽可能多地打印,而不必等到整个列表准备就绪

从GHCi看严格 您可以使用
:sprint
命令查看在
ghci
中对列表的多少进行评估,该命令将为尚未评估的零件(称为“thunks”)打印一个带有
的Haskell数据结构。您可以使用此选项查看如何在操作中评估
fibs

Prelude> let fibs = 1 : 1 : zipWith (+) fibs (tail fibs)
Prelude> :sprint fibs
fibs = _
Prelude> print $ fibs !! 10
89
Prelude> :sprint fibs
fibs = _
哎呀,这不是我们所期望的!事实上,这是一个缺乏单态限制的问题
fibs
获取多态类型

Prelude> :t fibs
fibs :: Num a => [a]
这意味着每次使用它时,它的行为类似于函数调用,而不是普通值。(在后台,GHC将
Num
类型类实例化为将字典传递给
fibs
;它的实现类似于
NumDictionary a->[a]
函数。)

为了真正理解发生了什么,我们需要显式地使
fibs
单态。我们可以通过从限制处于活动状态的模块加载它,或者给它一个显式的类型签名来实现这一点。让我们做后者:

Prelude> let fibs :: [Integer]; fibs = 1 : 1 : zipWith (+) fibs (tail fibs)
Prelude> :sprint fibs
fibs = _
Prelude> print $ fibs !! 10
89
Prelude> :sprint fibs
fibs = 1 : 1 : 2 : 3 : 5 : 8 : 13 : 21 : 34 : 55 : 89 : _
你可以看到列表中哪些部分需要评估,哪些部分没有得到第十个元素。您可以使用其他列表或其他惰性数据结构来更好地了解背景中发生的事情


另外,你可以看看这种懒惰。它更详细地介绍了
fibs
示例(带图表!),并讨论了如何将这种方法用于一般的记忆和动态编程。

从“懒惰”的角度解释了这种计算是如何工作的。这样做的一个副作用是,当您在GHCi中对其进行评估时,它实际上会传递到
打印
,它可以惰性地使用列表,将每个元素打印为可用元素。您定义的
fibs
列表实际上是无限的,您实际上无法计算全部内容;这很有用,也有点可怕。是否有运行时版本的
:sprint
,可能带有
a->IO Bool
?如果是这样的话,它将对没有副作用的普通评估产生令人不安的影响。@Cirdec:我相信这是存在的,就像
unsafecorce
unsafePerformIO
存在一样:它们是对抽象的逃避,只有在理解它的实际实现方式后才有意义。但我不确定它到底是如何工作的。