我有一个非常简单的斐波纳契函数:
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)
)
但我无法想象,它是如何运作的。
答案 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)
答案 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]
希望这有助于(某人)。