例如,我有一个像['a','b','c','d','e']的列表。
我想做这样的事情:
先用前两个元素做一些事情,f'a''b'
然后用f的返回值和列表中的下一个元素做同样的事情,结果= f'a''b',就像f结果'c'一样。然后f得到(结果'c')'d'等等。
我该怎么做这样的事情?
答案 0 :(得分:6)
首先让我们考虑你所拥有的函数f
。它需要某种累积值,一个普通值,并将它们组合成一个结果。因此,在类型签名中,我们会说a
表示累积值的类型,v
表示值的类型,r
表示结果的类型。< / p>
f :: a -> v -> r
现在我们要创建一个使用f
和值列表的折叠函数。
someFold :: (a -> v -> r) -> [v] -> ?
它应该返回什么?它应该产生一些结果类型r
,对吗?现在请注意,a
和r
实际上应该是相同的类型,因为我们会继续将f
的结果反馈到它的第一个参数中。
someFold :: (a -> v -> a) -> [v] -> a
现在缺少一件事。你是如何获得第一个a
的?有两种方法可以看待它。您只需选择第一个值,在这种情况下,a
与v
的类型相同,或者您指定基值,因此a
实际上可能与v
不同。让我们选择后者,因为那更有趣。我们还决定在此列表中从左向右移动。 (这就是你需要的,对吗?)
someFold :: (a -> v -> a) -> a -> [v] -> a
那么......我们如何实现它?它将是递归的,所以让我们从基本情况开始。
someFold f acc [] = acc
如果我们点击列表的末尾,那么我们已经积累了足够的,对吗?那很简单。那么递归案例怎么样?根据你所说的,我们应该在每一步将f
应用于“到目前为止的累计值”作为第一个参数,并将“列表的第一个值”作为第二个参数。 f acc x
。然后我们继续折叠,使用 作为我们新的“累积”值。
someFold f acc (x:xs) = someFold f (f acc x) xs
简单,对吗?但是......如果我们想像你说的那样做并通过获取列表的前两个值来启动函数怎么办?也很容易。只需取第一个元素,并将其称为原始的“基础”累加器!
someFold1 :: (v -> v -> v) -> [v] -> v
someFold1 f (x:xs) = someFold f x xs
请注意,由于此a
与此v
的类型相同,因此函数someFold1
具有非常有趣的类型签名。如果你理解这个解释,那么恭喜。我们刚刚实施了foldl
and foldl1
。
Prelude> foldl1 min "abcde" -- "abcde" is sugar for ['a','b','c','d','e']
'a'
在实际代码中,您应该实际使用foldl'
和朋友。
答案 1 :(得分:2)
听起来像是家庭作业。看看折叠。
答案 2 :(得分:2)
在这种情况下,折叠的问题是,它通常一次处理元素。您可以尝试手动滚动折叠。
假设您有函数f
,它一次获得两个元素,并且累加器(最后一次迭代的结果)被馈送。然后你的功能看起来像这样:
fold2 :: (a -> a -> b -> b) -> [a] -> b -> b
fold2 f accum (x:y:zs) = fold2 f (f x y) zs
fold2 _ accum [] = accum
fold2 _ _ _ = error "odd number of elements"
尝试理解这一点。 fold2
削减列表中的前两个元素,并将其输入f
。然后将结果作为新累加器传递给递归调用。这样做直到列表为空。