我有2个清单。它们相对于彼此的长度始终相同,可能看起来像这个玩具示例。实际内容是不可预测的。
val original = [1, 2, 0, 1, 1, 2]
val elements = ["a","b","c","d","e","f"]
我想创建以下列表:
val mappedList = [["c"],["a","d","e"],["b","f"]]
0 1 2
因此,模式是根据原始列表中相同位置元素的值对元素列表中的元素进行分组。知道如何在SML中实现这一点?我不是在寻找这种精确数据的硬编码解决方案,而是一般的数据。
答案 0 :(得分:2)
一种方法是首先编写一个函数,它接受一个有序对,如(2,"c")
和一对有序对,如
[(3,["a"]),(2,["b"]),(1,["a","e"])]
并返回一个修改后的列表,其中元素被添加到相应的列表中(或者如果不存在则创建一个新的(键,列表)对),以便结果如下所示:
[(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
答案 1 :(得分:2)
使用一般策略首先形成一对(k, vs)
的列表,其中k
是它们被分组的值,vs
是元素,然后可以单独提取元素。既然约翰这样做了,我还会添加另外两件事:
假设original : int list
,按排序顺序插入对:
fun group ks vs =
let fun insert ((k, v), []) = [(k, [v])]
| insert (k1v as (k1, v), items as ((k2vs as (k2, vs))::rest)) =
case Int.compare (k1, k2) of
LESS => (k1, [v]) :: items
| EQUAL => (k2, v::vs) :: rest
| GREATER => k2vs :: insert (k1v, rest)
fun extract (k, vs) = rev vs
in
map extract (List.foldl insert [] (ListPair.zip (ks, vs)))
end
这会产生与您的示例相同的结果:
- val mappedList = group original elements;
> val mappedList = [["c"], ["a", "d", "e"], ["b", "f"]] : string list list
我有点不确定是否"实际内容是不可预测的。"你的意思是" original
和elements
的类型是未知的。"所以:
假设original : 'a list
和某些cmp : 'a * 'a -> order
存在:
fun group cmp ks vs =
let fun insert ((k, v), []) = [(k, [v])]
| insert (k1v as (k1, v), items as ((k2vs as (k2, vs))::rest)) =
case cmp (k1, k2) of
LESS => (k1, [v]) :: items
| EQUAL => (k2, v::vs) :: rest
| GREATER => k2vs :: insert (k1v, rest)
fun extract (k, vs) = rev vs
in
map extract (List.foldl insert [] (ListPair.zip (ks, vs)))
end
使用树来存储对:
datatype 'a bintree = Empty | Node of 'a bintree * 'a * 'a bintree
(* post-order tree folding *)
fun fold f e Empty = e
| fold f e0 (Node (left, x, right)) =
let val e1 = fold f e0 right
val e2 = f (x, e1)
val e3 = fold f e2 left
in e3 end
fun group cmp ks vs =
let fun insert ((k, v), Empty) = Node (Empty, (k, [v]), Empty)
| insert (k1v as (k1, v), Node (left, k2vs as (k2, vs), right)) =
case cmp (k1, k2) of
LESS => Node (insert (k1v, left), k2vs, right)
| EQUAL => Node (left, (k2, v::vs), right)
| GREATER => Node (left, k2vs, insert (k1v, right))
fun extract ((k, vs), result) = rev vs :: result
in
fold extract [] (List.foldl insert Empty (ListPair.zip (ks, vs)))
end