我是 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接受一个参数)。
因此,answeris 是一个函数,即使它的参数没有显式出现。这是无点风格。
在非无点样式中,它看起来像
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)