我对组成的理解差距,类型不匹配

时间:2014-03-20 17:52:08

标签: function haskell lambda function-composition

在我的代码中,我需要按第二个元素的值对(Int, Int)的列表进行排序。

sorted = sortBy (\(_,a) (_,b) -> compare a b) listOfPairs很容易,但我讨厌像这样编写lambda,我需要在我的代码中的几个地方使用相同的lambda。

所以我尝试创建两个有用的函数,我最终会在很多地方使用如下(我无法在前奏中找到它们):

-- apply a function of one argument to two different arguments and return a pair
fBoth :: (a -> b) -> a -> a -> (b,b)
fBoth f a b = (f a, f b)

-- pass elements of a pair to a function of two arguments
expandPair :: (a -> b -> c) -> (a,b) -> c
expandPair f (a,b) = f a b

现在我想我可以将一些东西组合在一起传递给sortBy而不是一个丑陋的"提取器"拉姆达。但我不能让这些类型排成一行。

我觉得我想用(expandPair compare)撰写(fBoth snd),因为:t expandPair compare = (a,a) -> Ordering:t fBoth snd = (a,b) -> (a,b) -> (b,b)。我认为这个组合的结果类型是(a,b) -> (a,b) -> Ordering,我可以传递给sortBy,但它并不是很有效。

如果我尝试sortBy ((expandPair compare) . (fBoth snd)) [(1,2),(3,4)],它会给我类型错误。但让我感到困惑的是,如果我expandPair compare $ fBoth snd (1,2) (3,4)它确实有效,并按预期产生LT

很明显我在这里不理解关于功能组成的东西......我得到整个" g由f = g(f(x))&#34组成;但是我在这里让它无法正常工作。

或者,如果有更简单的方法来完成第二个元素排序对列表的特定任务,我也有兴趣听到它。

2 个答案:

答案 0 :(得分:8)

首先,您的expandPair函数与uncurry的定义完全相同,位于Prelude

这里的问题是fboth snd需要2个参数,因此普通的合成运算符不太适合这个任务。但是,你可以创建一个看起来很奇怪的运算符,让你“组合”一个带有两个参数的函数,该函数带有一个参数:

(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
(.:) = (.).(.)

用于记住这一点的简单助记符是,带有一个点的一侧的函数采用1个参数,与带有两个参数的函数的输出相同,这两个参数位于两侧。然后你可以把它写成

sortBy (uncurry compare .: fboth snd) [(1, 2), (3, 4)]

现在,我会说最好不要真正考虑.:运算符的工作原理,只看它的类型而不是它的定义。它经常派上用场,我经常使用它,我当然没有提出这个算子。


您也可以将其实现为

> :m +Data.Function
> sortBy (compare `on` snd) ...
-- sortBy (on compare snd) ...

但正如@mhwombat所指出的那样,Data.Ord有一个名为on compare comparing的别名:

> :m +Data.Ord
> sortBy (comparing snd) ...

这是我的偏好。


可能使这个操作员更清晰的东西:

> :t (id .: (,))
a -> b -> (a, b)
> (id .: (,)) 1 2
(1, 2)
> (fst .: (,)) 1 2
1
> (snd .: (,)) 1 2
2
> (negate .: (+)) 1 2
-3

答案 1 :(得分:0)

compare的特定情况下,您正在寻找来自Data.Function的函数的应用:

on