使用sortBy

时间:2018-09-25 20:32:45

标签: haskell

我正在尝试根据每个元组中的第4个元素对元组列表进行排序。第四个元素包含一个字符串,该字符串是一个人的名字。我想将包含相同名称的元组彼此相邻。元组排序之前的列表示例是:

[("A",100,"Q",3,"Todd",2.0),
 ("B",203,"R",3,"Rachel",1.66),
 ("B",273,"F",1,"Mike",2.66),
 ("A",200,"P",1,"Rachel",0.0),
 ("A",549,"D",3,"Todd",2.0),
 ("B",220,"S",3,"Todd",4.0),
 ("B",101,"M",3,"Jon",3.33),
 ("A",999,"N",3,"Rachel",1.33)]

我也希望它看起来像:

[("A",100,"Q",3,"Todd",2.0),
 ("A",549,"D",3,"Todd",2.0),
 ("B",220,"S",3,"Todd",4.0),
 ("B",203,"R",3,"Rachel",1.66),
 ("A",200,"P",1,"Rachel",0.0),
 ("A",999,"N",3,"Rachel",1.33),
 ("B",273,"F",1,"Mike",2.66),
 ("B",101,"M",3,"Jon",3.33)]

我需要的是将包含Todd的所有元组彼此相邻,以此类推。名称显示的顺序无关紧要,只要它们彼此相邻即可。

sortedList= show . sortBy byName . (map stringToTuple) . (map words) . lines

这是我在其中按排序方式调用的代码行。我知道我需要创建一个函数byName,该函数将以某种方式弄清楚元组是否具有通用名称。

任何帮助引导我正确编写byName方法的帮助。 谢谢

2 个答案:

答案 0 :(得分:6)

sortBy的类型开头:

> :t sortBy
sortBy :: (a -> a -> Ordering) -> [a] -> [a]

这意味着byName必须具有类型a -> a -> Ordering。在这种情况下,a是一个元组,其第五个元素为类型StringbyName将忽略其他字段。因此,您需要定义一个函数

type MyType = (String, Int, String, Int, String, Double)
byName :: MyType -> MyType -> Ordering
byName (_, _, _, _, a, _) (_, _, _, _, b, _) = ...

作为练习,我将用正确的表达式替换...

(回想一下Ordering是具有三个值LTEQGT的类型,其中byName a b == LT如果是a < bbyName a b == EQ(如果a == bbyName a b == GT,如果a > b。在您的情况下,只要它们具有相同的名称,两个元组就会比较相等。真正关心byName是否返回LT或否则返回GT。)

答案 1 :(得分:4)

虽然您可以自己解决类似问题,但是大多数所需功能已经可以通过Data.Ord获得。如果tuples是您的输入列表,则可以使用:

sortBy (comparing name) tuples

其中name是一个实用程序函数,定义为:

name (_, _, _, _, n, _) = n

这实际上是一个参数多态函数,因此您也可以将其称为fifth或类似的泛型函数。

您可以调用上面的表达式并对输出进行格式化,以查看它是否可以满足您的要求:

Prelude Data.Ord Data.List> putStrLn $ unlines $ show <$> sortBy (comparing name) tuples
("B",101,"M",3,"Jon",3.33)
("B",273,"F",1,"Mike",2.66)
("B",203,"R",3,"Rachel",1.66)
("A",200,"P",1,"Rachel",0.0)
("A",999,"N",3,"Rachel",1.33)
("A",100,"Q",3,"Todd",2.0)
("A",549,"D",3,"Todd",2.0)
("B",220,"S",3,"Todd",4.0)

与OP相比,这与要求的顺序相反,但是我将其作为练习来弄清楚如何更改排序顺序。有两种不同的方法可以做到这一点。