8

我一直在重用 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. 我有两个问题。

  1. 我很想知道是否有一个标准库函数可以做到这一点,而我只是找不到。
  2. 出于好奇,我想以无点风格重写此功能,但我遇到了很多麻烦。
4

3 回答 3

12
  1. Control.Arrow有这个功能(&&&)。它有一个“更通用”的类型,不幸的是,这意味着 Hoogle 没有找到它(也许这应该被认为是 Hoogle 中的一个错误?)。

  2. 您通常可以使用 自动计算出这种东西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

于 2013-01-16T03:28:05.533 回答
12

我很想知道是否有一个标准库函数可以做到这一点,而我只是找不到。

由于类型类,很容易错过,但看看Control.Arrow. Plain Arrows 不能被Arrow柯里化或应用,因此组合子必然是无点的。如果你专门研究它们(->),你会发现你想要的是这样的:

(&&&) :: (Arrow a) => a b c -> a b c' -> a b (c, c')

还有其他类似的函数,例如 for 的等效操作Either,它专门为(->)如下所示:

(|||) :: (a -> c) -> (b -> c) -> Either a b -> c

这与either.

出于好奇,我想以无点风格重写此功能,但我遇到了很多麻烦。

由于您要复制输入,因此您需要某种方式来实现这一点——最常见的方式是通过Applicative或的Monad实例(->),例如. 这本质上是一个隐式的内联monad,被拆分的参数是“环境”值。使用这种方法,成为,或成为,成为,并成为S 组合子\f g -> (,) <$> f <*> gReaderjoin f xf x xpurereturnconstfmap(.)(<*>) \f g x -> f x (g x)

于 2013-01-16T03:31:14.350 回答
6

实际上有很多方法可以做到这一点。最常见的方法是使用(&&&)函数 from Control.Arrow

f &&& g

但是,通常你有更多的函数或者需要将结果传递给另一个函数,在这种情况下使用 applicative 风格会更方便。然后

uncurry (+) . (f &&& g)

变成

liftA2 (+) f g

如前所述,这可以与多个功能一起使用:

liftA3 zip3 f g h
于 2013-01-16T04:01:27.987 回答