单独对每个位置的元组进行操作

时间:2014-11-16 18:05:13

标签: haskell mapping tuples

我想单独对每个位置的元组进行操作。例如

pair_of_sums (a1,b1) (a2,b2) = (a1+a2, b1+b2)

或更一般

sum_and_multiplication (a1,b1) (a2,b2) = (a1+a2, b1*b2)

有什么方法可以缩短它吗?像:

pair_of_sums = xxx (+)
sum_and_multiplication = yyy ((+),(*))

2 个答案:

答案 0 :(得分:4)

一种可能性是使用bifunctors包,它具有一些有用的方法和功能,可用于处理包括对在内的不同类型。

pair_of_sums = (<<.>>) . bimap (+) (+)

对于对,bimap采用两个函数并将第一个映射到第一个元素而第二个映射到第二个元素(因此,对于此特定实例,我们可以将其视为具有类型{{1 }})。我们可以在其定义中看到这一点

bimap :: (a -> a') -> (b -> b') -> (a, b) -> (a', b')

bimap f g ~(a, b) = (f a, g b) 基本上只是告诉Haskell在这里更加懒惰。这与我们的目的无关。)

在这种情况下,(<<.>>)采用一对函数和一对,它将第一对中的每个函数映射到第二对的相应值。对于此用途,我们可以将其视为键入~)。

我们可以再次查看此函数的源代码,以了解这对(<<.>>) :: (a -> a', b -> b') -> (a, b) -> (a', b')实例的工作原理:

(,)

要了解这些内容是如何组合在一起的,我们可以看到使用(f, g) <<.>> (a, b) = (f a, g b) 为我们提供了此功能(专门针对bimap的{​​{1}}实例,因为这就是我们正在使用的内容)< / p>

(,)

这为我们提供了一对函数,它们将给定值添加到对中每个位置的原始值。例如

Bifunctor

我们使用bimap (+) (+) :: Num a => (a, a) -> (a -> a, a -> a) 的合成允许我们将这对函数转换为一对函数,该函数采用一对并给出一对(符合我上面给出的类型签名)。

综合这些,我们可以更详细地了解通话λ> fst (bimap (+) (+) (3,5)) 10 13 λ> snd (bimap (+) (+) (3,5)) 100 105 中执行的减幅:

(<<.>>)

这与pair_of_sums (3, 5) (10, 100)类型类的工作方式非常相似,但它与pair_of_sums (3, 5) (10, 100) ((<<.>>) . bimap (+) (+)) (3, 5) (10, 100) -- Definition of pair_of_sums (\x -> (<<.>>) (bimap (+) (+) x)) (3, 5) (10, 100) -- Definition of (.) (<<.>>) (bimap (+) (+) (3, 5)) -- (10, 100) -- Function application bimap (+) (+) (3, 5) <<.>> (10, 100) -- Switch from prefix to infix ((+) 3, (+) 5) <<.>> (10, 100) -- After applying bimap for the (,) instance ((+) 3 10, (+) 5 100) -- Definition of (<<.>>) for the (,) instance (13, 105) -- Apply (+) 而不是Applicative一起使用(实际上,这就是类型类Bifunctor所属的原因被称为Functor)。 (<<.>>)类似于Biapplybimap类似于fmap

使用这种技术定义这些函数的一个很酷的事情是它们适用于所有(<<.>>)实例的类型构造函数,而不仅仅是(<*>)

答案 1 :(得分:3)

import Control.Arrow

f = ((uncurry (***) .) .) . (***)

main = do
    print $ f (+) (+) (1, 2) (3, 4) -- (4, 6)
    print $ f (+) (*) (1, 2) (3, 4) -- (4, 8)

以下是它的工作原理:

f                           = ((uncurry (***) .) .) . (***)
f op1 op2                   = uncurry (***) . (op1 *** op2)
f op1 op2  p1       p2      = uncurry (***) ((op1 *** op2) p1) p2
f op1 op2 (x1, y1)  p2      = uncurry (***) ((op1 *** op2) (x1, y1)) p2
f op1 op2 (x1, y1)  p2      = uncurry (***) (op1 x1, op2 y1) p2
f op1 op2 (x1, y1) (x2, y2) = (op1 x1 *** op2 y1) (x2, y2)
f op1 op2 (x1, y1) (x2, y2) = (op1 x1 x2, op2 y1 y2)