这是一个计算分割一美元的方法的程序。我不理解行c = a ++ zipWith (+) b c
,因为在此行之前未声明此行c,那么我们如何压缩b和c? (我是哈斯克尔的新手,很赞赏一个很好的解释)
import Data.List
change [] = 1 : repeat 0
change (d : ds) = c where
(a, b) = splitAt d (change ds)
c = a ++ zipWith (+) b c
result = change [1, 5, 10, 15, 20, 25, 50] !! 100
答案 0 :(得分:3)
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)-> fix ((a++) . zipWith (+) b))
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
(这是一个正确答案,BTW)。这可以说更容易理解。因此,您的问题已本地化为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返回 :
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 -- [1, 5, 10, 15, 20, 25, 50] 100
where
h n xs = ys where ys = zipWith (+) xs (replicate n 0 ++ ys)
-- = fix (zipWith (+) xs . (replicate n 0 ++))
答案 1 :(得分:2)
y = f y
相当于无限的应用链:`y = f(f(f(f(...
所以c = a ++ (zipWith (+) b c)
相当于c = a ++(zipWith(+)b(a ++(zipWith(+)b(...)))
答案 2 :(得分:2)
这是递归定义的特别复杂的用法。 “改变”和“c”都是根据自身定义的。
'change _'是一个无限长的单链整数列表。
这个'c'也是无限长的单一链接的整数列表。
'a ++ ...'的第一个元素是什么?如果'a'不是空的(这里它不是空的,因为列表传递给更改都是正的)那么它就是'a'的第一个元素。
实际上'a'在第一次更改中的长度为'1',然后是'5'然后是'10',直到最后'a'的长度为'50'。
所以'c'的第一个元素取自'a'。然后,一旦用完,'c'的以下元素来自'zipWith(+)b c'。
现在'b'和'c'是无限长的单链整数列表。
'b'的第一个元素来自'a'部分之后'change _'的递归调用的一部分。 'c'的第一部分是'a'部分。
将'a'部分的长度设为5,并按名称'p1'调用'a'。
c = (5 elements of 'a', call this p1)
++ (5 elements of zipWith (+) p1 b, call this p2)
++ (5 elements of zipWith (+) p2 (drop 5 b), call this p3)
++ (5 elements of zipWith (+) p3 (drop 10 b) ++...