Haskell- 接受函数列表的函数
Haskell- function that accepts a list of functions
我需要编写一个接受函数列表和一个值作为参数的函数。列表中的每个函数都必须依次应用于该值。
例如,如果我的函数被调用 compFuncs
...
compFuncs [f,g,h] val
等同于f(g(h val))
我已经知道使用 foldr
在这里很有用,我可以将 .
运算符放在函数列表中的每个函数之间,然后将其应用于 val
.但是,我无法完成,这是我的尝试...
compFuncs :: [(a->a->a)] -> a -> a
compFuncs [] val = val
compFuncs (x:xs) val = foldr //Im lost here
有人可以帮帮我吗?
(我相信您打算将类型写成 composeFuncs :: [a -> a] -> a -> a
,因为它是这样使用的。)
foldr
的工作原理是将列表的构造函数替换为您指定的替换项。例如,foldr (+) 0 [1,2,3]
的工作方式是获取列表 [1,2,3]
,它实际上构造为 1:2:3:[]
,并将 (:)
替换为 (+)
并将 []
替换为0
如下:
1 : 2 : 3 : []
1 + 2 + 3 + 0
如果您考虑要将函数列表 [f,g,h]
应用于某个值 \x -> f (g (h x))
,我们可以通过寻找 [=23] 的替代项来找到 foldr
=] 和 []
。首先,让我们使用组合:
\x -> f (g (h x))
= (definition of (.))
\x -> (f . g . h) x
= (eta reduction)
f . g . h
这很接近,但我们必须对空列表构造函数做些事情。我们需要用某种 "do nothing" 或 "empty" 函数替换它。幸运的是,我们有 id
,保证不会以任何方式改变结果:
f . g . h
= (definition of id)
f . g . h . id
现在我们可以看到弃牌了:
f . g . h . id
f : g : h : []
我们写成:
composeFuncs :: [a -> a] -> a -> a
composeFuncs = foldr (.) id
顺便说一句,可以像这样折叠的类型与一个用作 "identity" 的元素被称为幺半群*,而 a -> a
是 Endo 幺半群。
* 还有一个附加要求,即用于组合值的函数,如 (.)
对应 Endo
或 (+)
对应 Sum
,是关联的。您会注意到,这让我可以在不需要上面括号的情况下展示它们。
编辑
要了解此功能的另一种方法,让我们使用 GHC 7.8 的新类型孔功能。首先,我们从 composeFuncs
的定义开始,有一些漏洞:
composeFuncs :: [a -> a] -> a -> a
composeFuncs = foldr _f _z
当 GHC 类型检查这个时,我们得到一个类型错误,我将把它减少到相关行:
tmp.hs:6:22: Found hole ‘_f’ with type: (a -> a) -> (a -> a) -> a -> a …
tmp.hs:6:25: Found hole ‘_z’ with type: a -> a …
从_z
开始,类型a -> a
只有一个可能的函数,那就是id
。对于_f
,我们需要一个函数,将两个函数结合起来,给出一个新的函数。那当然是(.)
,所以我们写:
composeFuncs :: [a -> a] -> a -> a
composeFuncs = foldr (.) id
我需要编写一个接受函数列表和一个值作为参数的函数。列表中的每个函数都必须依次应用于该值。
例如,如果我的函数被调用 compFuncs
...
compFuncs [f,g,h] val
等同于f(g(h val))
我已经知道使用 foldr
在这里很有用,我可以将 .
运算符放在函数列表中的每个函数之间,然后将其应用于 val
.但是,我无法完成,这是我的尝试...
compFuncs :: [(a->a->a)] -> a -> a
compFuncs [] val = val
compFuncs (x:xs) val = foldr //Im lost here
有人可以帮帮我吗?
(我相信您打算将类型写成 composeFuncs :: [a -> a] -> a -> a
,因为它是这样使用的。)
foldr
的工作原理是将列表的构造函数替换为您指定的替换项。例如,foldr (+) 0 [1,2,3]
的工作方式是获取列表 [1,2,3]
,它实际上构造为 1:2:3:[]
,并将 (:)
替换为 (+)
并将 []
替换为0
如下:
1 : 2 : 3 : []
1 + 2 + 3 + 0
如果您考虑要将函数列表 [f,g,h]
应用于某个值 \x -> f (g (h x))
,我们可以通过寻找 [=23] 的替代项来找到 foldr
=] 和 []
。首先,让我们使用组合:
\x -> f (g (h x))
= (definition of (.))
\x -> (f . g . h) x
= (eta reduction)
f . g . h
这很接近,但我们必须对空列表构造函数做些事情。我们需要用某种 "do nothing" 或 "empty" 函数替换它。幸运的是,我们有 id
,保证不会以任何方式改变结果:
f . g . h
= (definition of id)
f . g . h . id
现在我们可以看到弃牌了:
f . g . h . id
f : g : h : []
我们写成:
composeFuncs :: [a -> a] -> a -> a
composeFuncs = foldr (.) id
顺便说一句,可以像这样折叠的类型与一个用作 "identity" 的元素被称为幺半群*,而 a -> a
是 Endo 幺半群。
* 还有一个附加要求,即用于组合值的函数,如 (.)
对应 Endo
或 (+)
对应 Sum
,是关联的。您会注意到,这让我可以在不需要上面括号的情况下展示它们。
编辑
要了解此功能的另一种方法,让我们使用 GHC 7.8 的新类型孔功能。首先,我们从 composeFuncs
的定义开始,有一些漏洞:
composeFuncs :: [a -> a] -> a -> a
composeFuncs = foldr _f _z
当 GHC 类型检查这个时,我们得到一个类型错误,我将把它减少到相关行:
tmp.hs:6:22: Found hole ‘_f’ with type: (a -> a) -> (a -> a) -> a -> a …
tmp.hs:6:25: Found hole ‘_z’ with type: a -> a …
从_z
开始,类型a -> a
只有一个可能的函数,那就是id
。对于_f
,我们需要一个函数,将两个函数结合起来,给出一个新的函数。那当然是(.)
,所以我们写:
composeFuncs :: [a -> a] -> a -> a
composeFuncs = foldr (.) id