麻烦理解Haskell中的折叠

时间:2015-01-17 20:01:07

标签: haskell

我希望有一个带有列表和数字的函数,并将该列表中的所有函数应用于该数字。 例如:

applyAll [(*3),(+1),\x->x+5] 2

会产生数字24。

我的问题是我必须用折叠来做,我还不太了解它。 LearnYouaHaskell帮助了一点,但我仍然很困惑。

有关如何执行此操作的任何帮助?

2 个答案:

答案 0 :(得分:3)

首先,让我们想一想applyAll

的类型
applyAll :: Num a => [(a -> a)] -> a -> a

在此过程中,您可以从列表中创建单个值。这称为折叠(或 fold )。您的示例表明applyAll应该从右侧开始,因为结果是

24 = 3 * (1 + (5 + (2)))

因此,它是右折Prelude(以及一些变体)中有两个函数可以折叠列表:foldlfoldr。后缀表示它是左折还是右折。如上所述,您需要正确的折叠。让我们看看foldr的类型:

foldr :: (a -> b -> b) -> b -> [a] -> b

文档指出(a -> b -> b)是折叠函数,从列表(类型a)和累加器(类型b)中获取值。然后是最初的值,最后是列表。

applyAll的第一个原型是

applyAll fns init = foldr ?? init fns

此时我们只使用这些类型。我们知道applyAll将列表作为第一个参数,将值作为第二个参数。我们知道foldr需要某种功能,值和列表。所以这个初稿恰好符合我们的目的。

我们现在快到了!缺少的是正确的折叠功能。请记住,我们正在寻找从列表中获取值的东西,以及累加器。但是,在我们的示例中,列表的值为(a -> a),累加器为a。所以我们的函数应该有类型

?? :: (a -> a) -> a -> a

幸运的是,这个功能实际上非常简单,众所周知,并且可能经常被您使用。想一想:函数应该采用另一个函数,一个值,并返回一个值。这听起来像一个简单的函数应用程序,确实($)是您正在寻找的函数。

将所有这些内容放在一起创建applyAll仅作为练习留给你,但此时,还有很多工作要做。

答案 1 :(得分:2)

正如我在评论中提到的那样:fold(或者,更常见的是,reduce)是一个高阶函数,它将两参数变换应用于列表的每个元素,另一个参数是前一个转换的返回值;因此“链接”了对列表中所有元素的转换。

使用这个逻辑,你可以直观地推断,如果你有一个函数列表,你的转换必须返回另一个函数。但它应该返回什么样的功能?事实证明,变换可以是函数组合操作本身。

示例(可能不是很惯用,因为我明确地避免使用无点符号 - 它可能会帮助您更轻松地理解代码):

-- composes a list of functions, the last element in the list being the innermost
applyAll :: [Int -> Int] -> (Int -> Int)
applyAll fs = foldr (.) id fs

-- usage:
main :: IO ()
main = print $ applyAll [(*3), (+1), \x->x+5] 2