斐波纳契功能如何扩展?

时间:2017-06-15 06:52:02

标签: haskell

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

fibs = 1 : scanl (+) 1 fibs

但我无法弄清楚它将如何扩展。如您所见,它是一个递归函数。

斐波那契序列号是(从一开始):

1, 1, 2, 3, 5, 8, 13, 21, n 

上面序列中的第二个数字(数字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)
                            )   

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

4 个答案:

答案 0 :(得分:3)

我们有

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

我们想看看,haskell如何评估表达式

  let fibs = 1 : scanl (+) 1 fibs

完全披露:正如表达式所写,它没有得到 完全评估(忽略可能的实施细节)。

为什么? Haskell是懒惰的,表达式被评估直到 他们达到“弱头正常形式”,这意味着,一个表达 被评估,直到我们得到一个结果表达式,这是正常的 form或者是数据构造函数或等待参数的lambda (即部分应用的功能)。

因此,与名称fibs绑定的值已经处于弱势正常状态 form,因为它是带参数的数据构造函数。

 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

然后它停止了,因为我们到达了一个数据构造函数。 head $ tail fibs是 现在很容易评估,无需评估案例表达式 进一步。希望这个结果也会被记忆,所以haskell现在知道, 可以将称为fibs的表达式计算为

1 : 1 : case fibs of { [] -> []; x:xs -> scanl (+) (1 + x) xs ; }

让我们问tail (tail fibs)

  tail (tail fibs)
= tail (1 : case fibs of { [] -> []; x : xs -> scanl (+) (1 + x) xs; }
= case fibs of { []-> []; x : xs -> scanl (+) (1 + x) xs; }
= scanl (+) (1 + 1) (tail fibs)
= 2 : case (tail fibs) of { [] -> [] ; x:xs -> scanl (+) (2 + x) xs; }
--  ^ data constructor

然后再次停止。结果将再次被记忆并且现在已经消失了 知道,表达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得到评估。所以我们直接将下一个元素作为传递给scanl的值作为第二个参数。对于接下来的元素,haskell存储计算,它将始终评估案例的第二个分支,因为我们在消费fibs时总是后面。这里的下一个斐波那契数字计算为f q x

当然,它不会是tail次呼叫的连续筹码 scanl的最后一个参数,但是会有一些方法以更直接的方式保持列表中的位置,因为我们已经计算过它。我使用了那些堆叠的tail调用来简化理解。

PS:对其中一条评论的补充答案:

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 : some computation, not yet done感谢懒惰。

答案 1 :(得分:1)

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

答案 2 :(得分:1)

Maybe this will be more clearer:

-- 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:...)

答案 3 :(得分:0)

一个帮助我意识到这件事情的观点 sequence是删除递归并手动构造它。至 更好地说明了这一点,我将使用以。开头的Fibonacci序列 2和3:

[2, 3, 5, 8, ...]

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

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

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

即,给出第一个成员,第二个成员3是 作为scanl的初始累加器给出的值。

因此,当递归开始时,序列为[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]

希望这有助于(某人)。