我希望有一个带有列表和数字的函数,并将该列表中的所有函数应用于该数字。 例如:
applyAll [(*3),(+1),\x->x+5] 2
会产生数字24。
我的问题是我必须用折叠来做,我还不太了解它。 LearnYouaHaskell帮助了一点,但我仍然很困惑。
有关如何执行此操作的任何帮助?
答案 0 :(得分:3)
首先,让我们想一想applyAll
:
applyAll :: Num a => [(a -> a)] -> a -> a
在此过程中,您可以从列表中创建单个值。这称为折叠(或 fold )。您的示例表明applyAll
应该从右侧开始,因为结果是
24 = 3 * (1 + (5 + (2)))
因此,它是右折。 Prelude
(以及一些变体)中有两个函数可以折叠列表:foldl
和foldr
。后缀表示它是左折还是右折。如上所述,您需要正确的折叠。让我们看看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