[英]Recursion over lists in Haskell
例如,我有一个像['a','b','c','d','e']这样的列表。
我想做这样的事情:
先用前两个元素做一些事情,f'a''b'
然后用f的返回值和列表中的下一个元素做同样的事情,结果= f'a''b',就像f结果'c'一样。 然后f得到(结果'c')'d',依此类推。
我该怎么做这样的事情?
首先,让我们考虑功能f
你带来了。 它需要某种累积值,一个普通值,并将它们组合成一个结果。 因此,在类型签名中,我们将a
表示累积值的类型, v
表示值的类型, r
表示结果的类型。
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
和foldl1
。
Prelude> foldl1 min "abcde" -- "abcde" is sugar for ['a','b','c','d','e']
'a'
在实际代码中,您应该使用foldl'
和朋友。
听起来像是家庭作业。 看看折叠。
在这种情况下,折叠的问题是,它通常一次处理元素。 您可以尝试手动滚动折叠。
假设你有你的函数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
。 然后将结果作为新累加器传递给递归调用。 这样做直到列表为空。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.