我是Haskell的新手,正在尝试使用sort
函数使用第一个元素对元组列表进行排序。所以,如果我有["a", "b", "a", "c", "c"]
,我会得到类似[(1,"b"), (2,"a"), (2,"c")]
的内容(如果数字相同,则按字母顺序排列)。
我该怎么做呢?我现在完全迷失了......我仍然试图进入'哈斯克尔的思维方式'。
答案 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),这与>>>
完全相同,只是它的参数被翻转:
<<<