【问题标题】:creating a list of lists based on another list content根据另一个列表内容创建列表列表
【发布时间】:2016-11-24 22:38:20
【问题描述】:

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

【问题讨论】:

  • 什么不能按预期工作?
  • 创建智能方法的过程。
  • 这看起来很像十天前回答的Partition a list into equivalence classes。在您的两个列表上调用ListPair.zip,并通过仅考虑数字的等价函数对对进行分组。您也可能会受到 Haskell 的 group / groupBy 的启发,尽管源代码可能有点难以阅读。

标签: sml


【解决方案1】:

一种方法是首先编写一个函数,该函数接受一个有序对,例如 (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

【讨论】:

  • 似乎键被假定为整数,而不是''a,并且至少从示例中它们应该从低到高排序。
  • @SimonShine 好点。由于您的回答充分解决了排序问题,并且 OP 可能需要有关出现顺序的信息,因此我将保持我的基本回答不变,尽管您启发了我更多关于如何对 group 的输出进行排序的信息。
  • 对,我想最后对它进行排序,但这更适用于你已经给出的代码。 :)
【解决方案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
    

【讨论】:

    猜你喜欢
    • 2020-04-28
    • 2020-11-04
    • 2018-06-20
    • 2015-12-02
    • 2018-10-24
    • 1970-01-01
    • 2019-06-06
    • 2017-07-24
    • 1970-01-01
    相关资源
    最近更新 更多