将列表排序为元组

时间:2012-05-02 09:16:02

标签: function sorting haskell

我是Haskell的新手,正在尝试使用sort函数使用第一个元素对元组列表进行排序。所以,如果我有["a", "b", "a", "c", "c"],我会得到类似[(1,"b"), (2,"a"), (2,"c")]的内容(如果数字相同,则按字母顺序排列)。

我该怎么做呢?我现在完全迷失了......我仍然试图进入'哈斯克尔的思维方式'。

1 个答案:

答案 0 :(得分:9)

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"])转换为我们感兴趣的内容:这些元素出现的次数,以及这些元素的单个代表(例如{{1} })。


这里的成语是我们正在使用管道:我们的输入进入一个函数,该函数的输出进入另一个函数,依此类推,直到该函数结束时管道产生我们作为我们自己的输出呈现的输出。请注意,这只能起作用,因为每个函数只接受一个参数((2, "a")接受两个参数,map是第一个参数,因此f接受一个参数。)

因此,map f是一个函数,即使它的参数没有明确出现。这是无点风格。

无点样式中,它看起来像

answer

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),这与>>>完全相同,只是它的参数被翻转:

<<<