【问题标题】:Recursion over lists in HaskellHaskell中列表的递归
【发布时间】:2011-07-16 22:14:06
【问题描述】:

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

【问题讨论】:

  • 您可能需要更清楚地了解您的函数的实际作用:f 的类型签名是什么样的?以['a','b','c'] 为例:你能准确地写出最终结果应该是什么样子吗?是(f (f 'a' 'b') 'c') 还是其他的?这对f 的签名意味着什么,一旦您知道这一点,您能否调整下面的折叠答案来帮助您完成它?
  • 通常我会告诉你类型签名,然后“停止。胡歌时间!”但是对于这种情况,我觉得你可能会更喜欢理解折叠。另见LYAH # folds

标签: list haskell recursion fold


【解决方案1】:

听起来像是家庭作业。看看折叠。

【讨论】:

  • 由于我是函数式编程的新手,而且haskell中没有循环,所以有点难以习惯。很好的折叠可能会起作用,谢谢。
  • 折叠实际上是函数式语言中基本的“循环”机制。学习它们,它会带你走很长的路。
  • -1 虽然我同意不应直接回答家庭作业问题,但我也认为答案应该有所帮助并尝试深入研究问题。
【解决方案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。然后将结果 this 作为新的累加器传递给递归调用。这样做直到列表为空。

【讨论】:

  • 好的,我知道了。但我这里还有另一个问题。在折叠内部,f(x y) 的返回值与列表中的类型不同。例如我在列表中有字符,但 f 返回 int 类型。
  • 啊!我有点误解了你的问题。我以为您想一次将两个元素输入 f.哎呀...我要删除这个答案。
  • 其实场景是这样的:f (g x) (g y) -> 我先把x和y这两个元素发给g,然后把它们的结果发给f。然后我想发送第三个元素,比如 g head(xs)。和它一样 f( (f (g x) (g y) ) head(xs) )。
【解决方案3】:

首先让我们考虑一下您拥有的函数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 有一个非常有趣的类型签名。如果你理解了这个解释,那么恭喜你。我们刚刚实现了foldl and foldl1

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

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

【讨论】:

  • 谢谢,这就是我想要理解的。
猜你喜欢
  • 1970-01-01
  • 2019-07-21
  • 1970-01-01
  • 1970-01-01
  • 2018-02-20
  • 1970-01-01
  • 2013-01-17
  • 2015-03-10
  • 1970-01-01
相关资源
最近更新 更多