一种方法是首先编写一个函数,该函数接受一个有序对,例如 (2,"c") 和一个有序对列表,例如
[(3,["a"]),(2,["b"]),(1,["a","e"])]
并返回一个修改后的列表,并将元素添加到适当的列表中(如果不存在,则创建一个新的 (key,list) 对),以便结果如下所示:
[(3,["a"]),(2,["c","b"]),(1,["a","e"])]
以下函数可以解决问题:
fun store ((k,v), []) = [(k,[v])]
| store ((k,v), (m,vs)::items) = if k = m
then (m,v::vs)::items
else (m,vs)::store ((k,v) ,items);
给定一个键列表和一个相应的值列表,您可以将最后一个函数折叠到相应的键和值的 zip 上:
fun group ks vs = foldl store [] (ListPair.zip(ks,vs));
例如,如果
val original = [1, 2, 0, 1, 1, 2];
val elements = ["a","b","c","d","e","f"];
- group original elements;
val it = [(1,["e","d","a"]),(2,["f","b"]),(0,["c"])] : (int * string list) list
请注意,如果需要,您可以根据键对该列表进行排序。
最后——如果你只想要组(反转以匹配它们在列表中的原始顺序),则以下工作:
fun groups ks vs = map rev (#2 (ListPair.unzip (group ks vs)));
例如,
- groups original elements;
val it = [["a","d","e"],["b","f"],["c"]] : string list list
编辑时:如果您希望根据键(而不是它们出现的顺序)对最终答案进行排序,您可以使用 @SimonShine 的想法并将数据存储在排序顺序,或者您可以对group 函数的输出进行排序。有点奇怪的是,SML 标准基础库lacks a built-in sort,但标准实现有自己的分类(而且很容易编写自己的)。例如,使用 SML/NJ 的排序,你可以这样写:
fun sortedGroups ks vs =
let
val g = group ks vs
val s = ListMergeSort.sort (fn ((i,_),(j,_)) => i>j) g
in
map rev (#2 (ListPair.unzip s))
end;
领先于预期:
- sortedGroups original elements;
val it = [["c"],["a","d","e"],["b","f"]] : string list list