我是 Haskell 的新手,正在尝试使用sort
函数对元组列表进行排序,使用它们的第一个元素。因此,如果我有,["a", "b", "a", "c", "c"]
我会得到类似的东西[(1,"b"), (2,"a"), (2,"c")]
(如果数字相同,则按字母顺序排列)。
我该怎么做呢?此刻我完全迷失了……我仍在尝试进入“Haskell 思维方式”。
import Data.List (sort, group)
import Control.Arrow ((&&&))
answer :: Eq a => [a] -> [(Int, a)]
answer = sort . map (length &&& head) . group . sort
但是因为你是初学者,可能要告诉你的有点多&&&
,所以我将它改写成这样:
import Data.List (sort, group)
answer :: Eq a => [a] -> [(Int, a)]
answer = sort . map f . group . sort
where f xs @ (x:_) = (length xs, x)
你会注意到我打sort
了两次电话。这是故意的。
最后sort
一个(左边的那个)对元组的输出列表进行排序,碰巧它按元组的第一个元素的升序排序,通过对元组的第二个元素进行排序来打破平局。
最初sort
的(右边的那个)对输入列表进行排序,因为它的作用是:它将相邻的相等元素group
分组到一个子列表中。(顺便说一句,这些子列表保证永远不会为空——否则在模式匹配中使用或忽略空列表选项是不安全的。)head
然后map f
将这些列表(例如["a", "a"]
)变成我们感兴趣的内容:这些元素出现的次数,以及这些元素的单个代表(例如(2, "a")
)。
这里的习惯用法是我们正在使用管道:我们的输入进入一个函数,该函数的输出进入另一个函数,依此类推,直到管道末端的函数产生输出,我们将其呈现为我们自己的输出. 请注意,这只有效,因为每个函数只接受一个参数(map
接受两个参数,f
是这些参数中的第一个,所以map f
接受一个参数)。
因此,answer
is 是一个函数,即使它的参数没有显式出现。这是无点风格。
在非无点样式中,它看起来像
answer xs = sort . map f . group . sort $ xs
where f xs @ (x:_) = (length xs, x)
或者
answer xs = sort $ map f $ group $ sort xs
where f xs @ (x:_) = (length xs, x)
或者
answer xs = sort (map f (group (sort xs)))
where f xs @ (x:_) = (length xs, x)
当它使您的代码更清晰时,使用无点样式是一个好主意。
如果您愿意,可以使用<<<
运算符(再次来自 Control.Arrow,抱歉)使数据流方向表面上更加明确:
import Data.List (sort, group)
import Control.Arrow ((<<<))
answer :: Eq a => [a] -> [(Int, a)]
answer = sort <<< map f <<< group <<< sort
where f xs @ (x:_) = (length xs, x)
有些人认为这是错误的方式,并希望首先“发生”的功能位于左侧。这些人可以使用>>>
(也来自 Control.Arrow),这与它的参数完全一样,<<<
只是它的参数被翻转了:
import Data.List (sort, group)
import Control.Arrow ((>>>))
answer :: Eq a => [a] -> [(Int, a)]
answer = sort >>> group >>> map f >>> sort
where f xs @ (x:_) = (length xs, x)