0

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

我该怎么做呢?此刻我完全迷失了……我仍在尝试进入“Haskell 思维方式”。

4

1 回答 1

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"])变成我们感兴趣的内容:这些元素出现的次数,以及这些元素的单个代表(例如(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)
于 2012-05-02T09:26:38.513 回答