我正在尝试根据每个元组中的第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
方法的帮助。
谢谢
答案 0 :(得分:6)
以sortBy
的类型开头:
> :t sortBy
sortBy :: (a -> a -> Ordering) -> [a] -> [a]
这意味着byName
必须具有类型a -> a -> Ordering
。在这种情况下,a
是一个元组,其第五个元素为类型String
; byName
将忽略其他字段。因此,您需要定义一个函数
type MyType = (String, Int, String, Int, String, Double)
byName :: MyType -> MyType -> Ordering
byName (_, _, _, _, a, _) (_, _, _, _, b, _) = ...
作为练习,我将用正确的表达式替换...
。
(回想一下Ordering
是具有三个值LT
,EQ
和GT
的类型,其中byName a b == LT
如果是a < b
, byName a b == EQ
(如果a == b
,byName 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相比,这与要求的顺序相反,但是我将其作为练习来弄清楚如何更改排序顺序。有两种不同的方法可以做到这一点。