我一直在重复使用lambda表达式,例如
\x -> (f x, g x)
其中我将相同的输入应用于两个函数并将结果封装在一对中。我可以写一个捕捉这个
的函数combine :: (a -> b) -> (a -> c) -> a -> (b,c)
combine f g x = (f x, g x)
现在上面的lambda表达式只是combine f g
。我有两个问题。
答案 0 :(得分:12)
Control.Arrow
具有此功能(&&&)
。它有一个“更一般”的类型,不幸的是,这意味着Hoogle没有找到它(可能这应该被认为是Hoogle中的一个错误?)。
您通常可以使用pointfree
自动计算此类内容,lambdabot
中的#haskell
作为插件。
例如:
<shachaf> @pl combine f g x = (f x, g x)
<lambdabot> combine = liftM2 (,)
liftM2
(r ->)
Monad
实例的(a -> b -> c) -> (r -> a) -> (r -> b) -> r -> c
类型为{{1}}。当然,还有许多其他方法可以根据您允许的基元来编写这一点。
答案 1 :(得分:12)
我很想知道是否有一个标准的库函数可以解决这个问题。
由于类型类很容易错过,但是look at Control.Arrow
。普通Arrow
无法进行计算或应用,因此Arrow
组合器必须是无点的。如果你将它们专门化为(->)
,你会发现你想要的是:
(&&&) :: (Arrow a) => a b c -> a b c' -> a b (c, c')
还有其他类似的功能,例如Either
的等效操作,专门用于(->)
,如下所示:
(|||) :: (a -> c) -> (b -> c) -> Either a b -> c
与either
相同。
出于好奇,我想以无点的方式重写这个功能,但我遇到了很多麻烦。
由于您正在复制输入,因此您需要某种方式来执行此操作 - 最常见的方法是通过Applicative
的{{1}}或Monad
实例,例如{ {1}}。这本质上是一个隐式的内联(->)
monad,被拆分的参数是“环境”值。使用此方法,\f g ->
变为(,)
<$> f <*> g(,)
,Reader
或join f x
变为f x x
,pure
变为return
,{{1 }成为S combinator const
。
答案 2 :(得分:6)
实际上有很多方法可以做到这一点。最常见的方法是使用(&&&)
中的Control.Arrow
函数:
f &&& g
但是,通常你有更多的功能或需要将结果传递给另一个函数,在这种情况下使用applicative样式会更方便。然后
uncurry (+) . (f &&& g)
变为
liftA2 (+) f g
如上所述,这可以与多个功能一起使用:
liftA3 zip3 f g h