Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/actionscript-3/7.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 - Fatal编程技术网

Haskell 斐波那契函数如何展开?

Haskell 斐波那契函数如何展开?,haskell,Haskell,我有一个非常简单的斐波那契函数: fibs = 1 : scanl (+) 1 fibs 但我不知道它将如何扩展。如您所见,这是一个递归函数 斐波那契序列号为(以1开头): 上面序列中的第二个数字(数字1),对我来说不清楚,为什么1不是2 请给我解释一下,fibs将如何扩展 更新 我试着展开如下: fibs = 1 : scanl (+) 1 fibs scanl (+) 1 [1](fibs) = 1 : ( case [1]

我有一个非常简单的斐波那契函数:

fibs = 1 : scanl (+) 1 fibs
但我不知道它将如何扩展。如您所见,这是一个递归函数

斐波那契序列号为(以1开头):

上面序列中的第二个数字(数字1),对我来说不清楚,为什么
1
不是
2

请给我解释一下,
fibs
将如何扩展

更新

我试着展开如下:

fibs = 1 : scanl (+) 1 fibs

scanl (+) 1 [1](fibs) = 1 : (
                                case [1] of
                                    [] -> []
                                    x:xs -> scanl (+) (1+1) [1](fibs)
                            )

scanl (+) 2 [1](fibs) = 2 : (
                                case [1] of
                                    [] -> []
                                    x:xs -> scanl (+) (2+1) [1](fibs)
                            )

scanl (+) 3 [1](fibs) = 3 : (
                                case [1] of
                                    [] -> []
                                    x:xs -> scanl (+) (3+1) [1](fibs)
                            )   
如您所见,尾部总是返回
[1](fibs)
,这当然是错误的。
最后一个表达式应为:

scanl (+) 3 [2](fibs) = 3 : (
                                case [2] of
                                    [] -> []
                                    x:xs -> scanl (+) (3+2) [3](fibs)
                            )   

但我无法想象,它是如何工作的。

扫描和折叠除了一件事之外是相似的。在“折叠”中,结果是最终值,但在“扫描”中,它是所有中间值直到最终值的列表。
当我们将第一个数字连接到列表的前面时,第一个斐波那契数字将是1。第二个斐波那契数也将是1(扫描的第二个参数),之后每个斐波那契数都是前一个数(扫描的运行总数)和前一个数的总和。

我们有

fibs = 1 : scanl (+) 1 fibs
并且可以查找
scanl
的定义

scanl                   :: (b -> a -> b) -> b -> [a] -> [b]
scanl                   = scanlGo
  where
    scanlGo           :: (b -> a -> b) -> b -> [a] -> [b]
    scanlGo f q ls    = q : (case ls of
                               []   -> []
                               x:xs -> scanlGo f (f q x) xs)
在下面的分析中,我们将把
scanlGo
看作
scanl

λ> 2 : (scanl (+) 3 [2])
[2,3,5]
我们想看看haskell如何计算表达式

  let fibs = 1 : scanl (+) 1 fibs
完全披露:由于表达是书面的,它没有得到 已评估(忽略可能的实现细节)

为什么呢?Haskell是懒惰的,表达式的计算直到 他们达到了“弱头范式”,也就是说,一个表达式 直到得到一个结果表达式,这是正常的 窗体,它是等待参数的数据构造函数或lambda (即部分应用的功能)

因此绑定到名称fibs的值已经处于弱头法线中 表单,因为它是带有参数的数据构造函数

 1 : scanl (+) 1 fibs
-- ^ this is the data constructor
所以要让haskell在这里评估任何东西,我们需要刺激它 有一点

让我们从

  tail fibs
您不能在ghci中键入,因为它将尝试打印它,这将 进一步尝试在最终阶段对其进行评估。所以如果你想做实验 对于ghci,使用

let t = tail fibs
head t
那么如何计算表达式
tail fibs
?大致上 像这样:

    tail fibs
  = tail (1 : scanl (+) 1 fibs)
  = scanl (+) 1 fibs
  = 1 : case fibs of { [] -> []; x:xs -> scanl (+) (1 + x) xs ; }
  --  ^ data constructor
在这里,当我们到达一个数据构造函数时,它停止了<代码>头$尾小谎是 现在易于计算,无需计算任何case表达式 进一步的哈斯克尔现在知道,这个结果也有望被记住, 称为
fibs
的表达式可以计算为

1 : 1 : case fibs of { [] -> []; x:xs -> scanl (+) (1 + x) xs ; }
让我们来询问
尾部(尾部小谎)

在这里它又停止了。同样,结果将被记录下来,现在haskell 知道表达式fibs可计算为:

1 : 1 : 2 : case (tail fibs) of { [] -> []; x:xs -> scanl (+) (2+x) xs; }
现在是这样的,如果你要求更多的元素,还记得haskell吗 在达到标准形式或数据类型后,不会进一步计算表达式 构造函数或部分应用的函数,如果您不要求的话

旧答案:

基本上是这样

fibs = 1 : scanl (+) 1 fibs
fibs = 1 : 1 : scanl (+) 2 (tail fibs)
fibs = 1 : 1 : 2 : scanl (+) 3 (tail (tail fibs))
fibs = 1 : 1 : 2 : 3 : scanl (+) 5 (tail (tail (tail fibs)))
fibs = 1 : 1 : 2 : 3 : 5 : scanl (+) 8 (tail (tail (tail (tail fibs))))
在计算下一个列表元素时,
scanl
get evaluation。所以我们直接得到下一个元素作为第二个参数传递给scanl的值。对于随后的元素,haskell存储计算,它将始终计算案例的第二个分支,因为我们在使用FIB时总是落后一个元素。这里,下一个斐波那契数计算为
fqx

当然,它不会是 scanl的最后一个参数,但是有一些方法可以更直接地保持列表中的位置,正如我们已经计算过的那样。我使用了那些堆叠的
tail
调用,以便于理解

附:对其中一条评论的补充回答:

1: tail fibs = scanl (+) 1 fibs
2:           = scanl (+) 1 (1 : scanl (+) 1 fibs)
3:           = 1 : scanl (+) 2 (scanl (+) 1 fibs)
-- with the equation from line 1 we can do
4:           = 1 : scanl (+) 2 (tail fibs)

5: tail (tail fibs) = scanl (+) 2 (tail fibs)
6:                  = scanl (+) 2 (1 : scanl (+) 2 (tail fibs))
7:                  = 2 : scanl (+) 3 (scanl (+) 2 (tail fibs))
-- with the equation from line 5 we can do
8:                  = 2 : scanl (+) 3 (tail (tail fibs))
等等

PSS:在您尝试扩展它时,您设置了
fibs=[1]
,这是错误的。它是
1:一些计算,由于懒散还没有完成。

也许这会更清楚:

-- scanl f z [x1, x2, ...] == [z, z `f` x1, (z `f` x1) `f` x2, ...]
let fibs = 1 : scanl (+) 1 fibs

   fibs
=> 1 : scanl (+) 1 fibs
=> 1 : scanl (+) 1 (1:...)
=> 1 : 1 : scanl (+) (1 + 1) (tail $ 1:1:...)
=> 1 : 1 : scanl (+) 2 (1:...)
=> 1 : 1 : 2 : scanl (+) (2 + 1) (tail $ 1:2:...)
=> 1 : 1 : 2 : scanl (+) 3 (2:...)
=> 1 : 1 : 2 : 3 : scanl (+) (3 + 2) (tail $ 2:3:...)
=> 1 : 1 : 2 : 3 : scanl (+) 5 (3:...)
=> 1 : 1 : 2 : 3 : 5 : scanl (+) (5 + 3) (tail $ 3:5:...)
=> 1 : 1 : 2 : 3 : 5 : scanl (+) 8 (5:...)
=> 1 : 1 : 2 : 3 : 5 : 8 : scanl (+) (8 + 5) (tail $ 5:8:...)
=> 1 : 1 : 2 : 3 : 5 : 8 : scanl (+) 13 (8:...)
=> 1 : 1 : 2 : 3 : 5 : 8 : 13 : scanl (+) (13 + 8) (tail $ 8:13:...)
=> 1 : 1 : 2 : 3 : 5 : 8 : 13 : scanl (+) 21 (13:...)
=> 1 : 1 : 2 : 3 : 5 : 8 : 13 : 21 : scanl (+) (21 + 13) (tail $ 13:21:...)
=> 1 : 1 : 2 : 3 : 5 : 8 : 13 : 21 : scanl (+) 34 (21:...)

一个帮助我认识到这个世界正在发生什么的视角 顺序是移除递归并手动构造它。到 为了更好地说明这一点,我将使用从 2和3:

这里所有的术语都不同,因此更容易区分 在他们之间

首先,生成前两个成员不需要递归:

λ> 2 : (scanl (+) 3 [])
[2,3]
也就是说,给出了第一个成员,第二个成员
3
是 作为
scanl
的初始累加器给出的值

λ> 2 : (scanl (+) 3 [2])
[2,3,5]
因此,当递归开始时,序列是
[2,3]
。此时 累加器的值为3,是序列的第一项 是
scanl
使用的下一个值

λ> 2 : (scanl (+) 3 [2])
[2,3,5]
因此,下一个输出是预期的
3+2=5

在此之后,累加器的值为5,下一个值为 scanl
消耗的是3,因此下一个输出是8

λ> 2 : (scanl (+) 3 [2, 3])
[2,3,5,8]

希望这能对(某人)有所帮助。

当我阅读《第一原理》中的Haskell编程时,有一个相同的表达式,我又花了几章时间才最终理解它是如何工作的。是的,我正在阅读这本书。顺便说一句,它不是递归函数,因为它根本不是函数。你可以称它为递归列表,或者(也许更清楚地说)递归定义的列表。
tail fibs=1:scanl(+)2(tail fibs)
什么是
tail fibs
?它太难理解了,我需要一点时间来理解它。但是非常感谢你的帮助。还有一个关于
(尾部小谎)
的问题。考虑下面的表达式<代码> FIB=1:1:2:SCALL(+)3(尾部(尾部FIB))< /代码>,它是如何得到值<代码> 2 < /代码>的?
scan
函数在尾部调用fibs,它如何从const单元格中获取最后一个值?
λ> 2 : (scanl (+) 3 [2, 3])
[2,3,5,8]