change [] = 1 : repeat 0
change (d : ds) = c where
(a, b) = splitAt d (change ds)
c = a ++ zipWith (+) b c
那么,
result = (!! 100) $ xs
where
xs = change [1, 5, 10, 15, 20, 25, 50]
= let
g (a,b) = let c = a ++ zipWith (+) b c in c
in
g . splitAt 1 . change $ [5, 10, 15, 20, 25, 50]
= g . splitAt 1 .
g . splitAt 5 . change $ [10, 15, 20, 25, 50]
= ....
= let h n = g . splitAt n
in
h 1 . h 5 . h 10 . h 15 . h 20 . h 25 . h 50 . (1:) . repeat $ 0
或者,更简单地说,
Prelude> (!! 100) $ foldr h (1:cycle [0]) [1, 5, 10, 15, 20, 25, 50]
1239
(这是一个正确的回答,顺便说一句)。这可能更容易理解。因此,您的问题被局限于g
定义,
g (a,b) = let c = a ++ zipWith (+) b c in c
关于 Haskell 的定义,值得一提的是它们是递归的(与 Scheme 中的 letrec 相等,而不是 let)。在这里,由于 c 是惰性消耗的,所以它的定义表明它由两部分构成,即 a ++ ...,因此首先消耗 a。这有效是因为 a 不依赖于 c。计算 a 不需要任何关于 c 的知识。
在 zipWith (+) b c 中,c 实际上是指向正在被定义的序列的指针,length a 从生产点 rest 处倒退 notches。
g (a,b) =
let c = a ++ rest
rest = zipWith (+) b c
in c
我们有
h n xs = g (splitAt n xs)
,这描述了将输入列表的总和向前移动
n
个刻度的结果:
x1 x2 x3 x4 x5 x6 x7 x8 ................ xs A
y1 y2 y3 y4 y5 .......... ys B
y1 y2 y3 y4 y5 y6 y7.................... ys == A + B
这表明可以通过改善访问的局部性来重新编写
h
,
change ds n = foldr h (1:cycle [0]) ds !! n
where
h n xs = ys where ys = zipWith (+) xs (replicate n 0 ++ ys)