在我的代码中,我需要按第二个元素的值对(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组成;但是我在这里让它无法正常工作。
或者,如果有更简单的方法来完成第二个元素排序对列表的特定任务,我也有兴趣听到它。
答案 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