Haskell 中的列表递归

发布于 2024-10-22 01:59:25 字数 197 浏览 2 评论 0原文

例如,我有一个类似 ['a','b','c','d','e'] 的列表。
我想做这样的事情:
首先对前两个元素进行处理,f 'a' 'b'
然后对 f 的返回值和列表中的下一个元素执行相同的操作,结果 = f 'a' 'b',就像 f 结果 'c' 一样。然后 f resultof(result 'c') 'd' 等等。
我怎样才能做这样的事情?

For instance, i have a list like ['a','b','c','d','e'].
I want to do something like this:
First do something with the first two elements, f 'a' 'b'
Then do the same thing with the return value of f and next element in the list, result = f 'a' 'b', lets say like f result 'c'. Then f resultof(result 'c') 'd' and so on.
How can i do something like this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

忘羡 2024-10-29 01:59:25

首先让我们考虑一下您拥有的函数f。它需要某种累积值(一个简单的值),并将它们组合成一个结果。因此,在类型签名中,我们将 a 表示累加值的类型,v 表示值的类型,r 表示> 用于结果的类型。

f :: a -> v -> r

现在我们要创建一个使用 f 和值列表的折叠函数。

someFold :: (a -> v -> r) -> [v] -> ?

它应该返回什么?它应该产生一些结果类型r,对吧?现在请注意,ar 实际上应该是相同的类型,因为我们不断将 f 的结果再次输入到它的第一个参数中。

someFold :: (a -> v -> a) -> [v] -> a

现在缺少一件事。如何获得第一个a?有两种方法可以看待这个问题。您可以选择第一个值,在这种情况下,av 的类型相同,或者您指定一个基值,因此 a 可以实际上与 v 不同。我们选择后者,因为后者更有趣。我们还决定在此列表中从左向右移动。 (这就是您所需要的,对吧?)

someFold :: (a -> v -> a) -> a -> [v] -> a

那么...我们如何实现它?它将是递归的,所以让我们从基本情况开始。

someFold f acc [] = acc

如果我们到达了列表的末尾,那么我们已经积累了足够的,对吧?那很容易。那么递归的情况又如何呢?根据您的说法,在每一步中,我们都应该将 f 应用于“到目前为止的累计值”作为第一个参数,并将“列表的第一个值”作为第二个参数。 f acc x。然后我们继续折叠,使用that作为我们新的“累积”值。

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

请注意,对于这种特殊情况,由于 av 的类型相同,因此函数 someFold1 具有非常有趣的类型签名。如果您理解了这个解释,那么恭喜您。我们刚刚实现了 foldlfoldl1

Prelude> foldl1 min "abcde" -- "abcde" is sugar for ['a','b','c','d','e']
'a'

在实际代码中,您实际上应该使用 foldl' 和朋友们。

First let's consider that function f that you have. It takes some sort of accumulated value, a plain value, and combines them into a result. So, in the type signature, we'll say a for the type of the accumulated value, v for the type of the value, and r for the type of the result.

f :: a -> v -> r

Now we want to create a folding function that uses f and a list of values.

someFold :: (a -> v -> r) -> [v] -> ?

What should it return? It should yield something of the resultant type r, right? Notice now that a and r should actually be the same type, since we keep feeding the result of f into it's first argument again.

someFold :: (a -> v -> a) -> [v] -> a

Now one thing's missing. How do you get the very first a? There are two ways to look at that. Either you just pick the first value, in which case a is the same type as v, or you specify a base value, so a could actually be different than v. Let's go with the latter, since that's more interesting. Let's also decide to move left to right in this list. (That's what you need, right?)

someFold :: (a -> v -> a) -> a -> [v] -> a

So...how do we implement it? It'll be recursive, so let's start with the base cases.

someFold f acc [] = acc

If we hit the end of the list, then we've accumulated enough, right? That was easy. So how about the recursive case? From what you said, at each step we should apply f to the "accumulated value so far" as the first argument, and the "first value of the list" as the second. f acc x. Then we keep folding, using that as our new "accumulated" value.

someFold f acc (x:xs) = someFold f (f acc x) xs

Easy, right? But...what if we want to do like you said and start the function by taking the first two values of the list? Also easy. Just take the first element, and call it the original "base" accumulator!

someFold1 :: (v -> v -> v) -> [v] -> v
someFold1 f (x:xs) = someFold f x xs

Notice that since a is the same type as v for this special case, the function someFold1 has a very amusing type signature. If you understood this explanation, then congrats. We've just implemented foldl and foldl1.

Prelude> foldl1 min "abcde" -- "abcde" is sugar for ['a','b','c','d','e']
'a'

In real code, you should actually use foldl' and friends.

不再让梦枯萎 2024-10-29 01:59:25

听起来像是家庭作业。看看褶皱。

Sounds like homework. Take a look at folds.

何其悲哀 2024-10-29 01:59:25

在这种情况下,折叠的问题是,它通常一次处理一个元素。您可以尝试手动折叠。

假设您有函数 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 中。然后将结果作为新的累加器传递给递归调用。执行此操作直至列表为空。

In this case, the problem with a fold is, that it usually processes on element at a time. You could try to manually roll a fold.

Assume, you have your function f, that gets two elements at a time and the accumulator (the result of the last iteration) fed. Then you function looks like this:

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"

Try to understand this. fold2 shaves the top two elements of the list of and feeds it into f. The result this is then passed as the new accumulator to the recursive call. This is done until the list is empty.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文