根据其他列表内容创建列表列表

时间:2016-11-23 15:01:20

标签: sml

我有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中实现这一点?我不是在寻找这种精确数据的硬编码解决方案,而是一般的数据。

2 个答案:

答案 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是元素,然后可以单独提取元素。既然约翰这样做了,我还会添加另外两件事:

  1. 假设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
    

    我有点不确定是否"实际内容是不可预测的。"你的意思是" originalelements类型是未知的。"所以:

    假设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
    
  2. 使用树来存储对:

    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