实际上,因为 Haskell 的懒惰是由需要调用的,所以定义了
xs = 1 : map (+1) xs
然后调用 egprint xs
继续为
xs
xs where xs = 1 : map (+ 1) xs
xs where xs = x1 : xs2 ; x1 = 1 ; xs2 = map (+ 1) xs
x1 : xs2 where xs2 = map (+ 1) (x1 : xs2) ; x1 = 1
x1 : xs2 where xs2 = (x1+1) : map (+ 1) xs2 ; x1 = 1
x1 : xs2 where xs2 = x2 : xs3 ; x2 = x1+1 ; xs3 = map (+ 1) xs2 ; x1 = 1
1 : x2 : xs3 where xs3 = map (+ 1) (x2 : xs3) ; x2 = 2
1 : x2 : xs3 where xs3 = (x2+1) : map (+ 1) xs3 ; x2 = 2
1 : 2 : x3 : xs4 where xs4 = map (+ 1) (x3 : xs4) ; x3 = 3
1 : 2 : 3 : x4 : xs5 where xs5 = map (+ 1) (x4 : xs5) ; x4 = 4
.....
这样就不会n+1
通过重复添加1
s 来重复计算。n
已经计算过了。
我们只是给每个地方一个名字,所以这个名字首先包含一个要执行的计算,然后是它的值。由于名称在其他计算中共享,因此不会重复计算其值。
同样的事情发生在斐波那契数列的计算上。
所以 thenxs
确实生成了 list [1,2..]
。重复添加的1
过程自然表示为iterate (+1) 1
,正如在另一个答案中已经指出的那样。
我们也可以将其定义为
nums1 = xs where
ys = 1 : ys
xs = zipWith (+) (0 : xs) ys
这也许就是你想要表达的。
通过 zip 定义列表并使其自身移动一步(如在 Fibonacci 列表定义中),映射函数可以在定义当前元素的同时同时访问列表的两个先前元素。
类似地,通过压缩自身移动一步和另一个列表(如nums1
上面的定义)来定义列表,使映射函数在定义当前元素的同时访问前一个元素。
我们说“映射函数”是因为zipWith
它是二进制的map
:
zipWith f xs ys = map (\(x,y) -> f x y) (zip xs ys)