我有一个二进制函数列表,例如(a -> b -> a)
和项目列表[b]
。我需要按如下方式生成转换(a -> a)
。对于每个b,我需要部分应用函数并生成它们的组合,这给了我一个与bs长度相同的一元函数列表。将这些作品中的每一个称为处理器;我接下来需要编写处理器。例如,如果我有函数[f0,f1]
和项[b0,b1]
,我需要函数(f1 b1) . (f0 b1) . (f1 b0) . (f0 b0)
。我可以看到我在bs上折叠,但这也看起来像是外部产品的组合。那么,Haskell是否提供了一种巧妙的方法来生产某种外部产品? (当然,订单很重要。)如果是这样,请用一个完整新手可以理解的术语解释。
答案 0 :(得分:3)
有关仅使用标准功能的解决方案,请参阅ThreeFx's answer。
您还需要使用(.)
折叠部分应用的函数列表。
首先,获取部分应用函数的列表:
-- If f :: a -> b -> a, then (`f` b) :: a -> a
[ (`f` b) | b <- [b0, b1], f <- [f0, f1] ]
-- Result: [(`f0` b0), (`f1` b0), (`f0` b1), (`f1` b1)]
(使用fs <*> bs
会很好,但您需要将f
部分应用于其第二个参数,而不是第一个,并且结果列表的顺序错误对于组成步骤。)
接下来,使用(.)
折叠该列表。我永远不会完全记住使用哪个折叠(或如何使用它来确保您需要的正确顺序),所以这是一个自定义递归函数,我知道这是正确的:
composeAll :: [(a -> a)]
composeAll [] = id
composeAll (f:fs) = composeAll fs . f
全部放在一起:
fs = [f0, f1, f2]
bs = [b0, b1, b2]
f = composeAll [(`f` b) | b <- bs, f <- fs]
答案 1 :(得分:3)
您可以使用<**>
(位于Control.Applicative
)和fold
结果:
combine :: [(a -> b -> a)] -> [b] -> (a -> a)
combine fs bs = foldr (flip (.)) id $ bs <**> (map flip fs)
让我们来看看组件:
map flip fs
这只是将函数从a -> b -> a
翻转到b -> a -> a
,以便我们稍后可以ap
。
bs <**> (map flip fs)
-- ex: [b0, b1] <**> [f0, f1] == [f0 b0, f1 b0, f0 b1, f1 b1]
此功能部分将所有bs
应用于fs
并将其放入列表中。我们现在要做的就是使用合成运算符fold
来(.)
结果。请注意,使用flip (.)
我们会反转合成顺序,因为<**>
生成的列表与我们想要的相反:
foldr (flip (.)) id $ it -- alternatively foldr1
-- ex: foldr (flip (.)) id [f0 b0, f1 b0, f0 b1, f1 b1] ==
-- (f1 b1) . (f0 b1) . (f1 b0) . (f0 b0)