了解用户定义的附加列表标准ml

时间:2018-03-06 02:52:12

标签: sml ml mosml

我无法理解标准ML中列表的实现。以下是它的定义方式:

附加列表是列表抽象数据类型的(简单)实现,它使构造便宜(O(1)),但使破坏变得昂贵(O(n))。 'a alistNN和'alist类型定义如下:

 datatype 'a alistNN = Sing of 'a | Append of 'a alistNN * 'a alistNN
 datatype 'a alist = Nil | NonNil of 'a alistNN

'alistNN类型表示“非零”附加列表,而'alist类型表示任意(nil或 非零)附加清单。

我对如何使用这些列表/制作这些列表感到困惑。例如,我必须编写一个定义为的函数:

'a alist -> 'a alist -> 'a alist that appends to append lists. 

理解此列表定义将获得任何帮助。

2 个答案:

答案 0 :(得分:3)

此数据结构表示带有树的列表,其中每个内部节点表示其子节点的连接,每个叶子是一个元素。例如,这是列表[1,2,3,4]的两种可能的表示形式:

[ref]

附加这些数据结构非常容易;您只需将它们与新的users: { uid: { name: "user1", items: { itemID1: { name: "item1" }, itemID2: { name: "item2" } } } } 节点链接在一起即可。我们可以附上上面的两个例子:

val L1 = Append (Append (Sing 1, Sing 2), Append (Sing 3, Sing 4))

   *
  / \
 *   *
/ \ / \
1 2 3 4

val L2 = Append (Append (Sing 1, Append (Sing 2, Sing 3)), Sing 4)

    *
   / \
  *   4
 / \
1   *
   / \
  2   3

显然你也必须在Append中将它们包装起来,但我会把它留给你。

答案 1 :(得分:1)

使用普通列表

datatype 'a normal_list = Nil | Cons of 'a * 'a normal_list

您的Cons运算符前置单个元素是 O(1),但附加两个列表 O(n)

fun append (Nil, ys) = ys
  | append (xs, Nil) = xs
  | append (Cons (x, xs), ys) = Cons (x, append (xs, ys))

使用这些附加列表,

datatype 'a alistNN = Sing of 'a | Append of 'a alistNN * 'a alistNN
datatype 'a alist = Nil | NonNil of 'a alistNN

您的Append运算符现在是 O(1),但 cons 变得更难 O(n),因为正如你所说,它需要销毁列表来重建它,因为数据结构的头部不再是第一个元素,而是最近附加列表的点。

  

我对如何使用这些列表/制作这些列表感到困惑。例如,我必须编写一个定义为的函数:

'a alist -> 'a alist -> 'a alist
     

附加到附加列表。

编辑:澄清了这一部分。)您已经有了一个构造函数Append : 'a alistNN * 'a alistNN -> 'a alistNN。要制作一个适用于' alist 的工具,您必须根据' alist 的不同情况进行模式匹配;只有当两个列表都是 NonNil 时才能使用Append(因为空列表不能表示为' a alistNN 。操作数为Nil的情况可以单独处理;

fun append Nil ys = ys
  | append xs Nil = xs
  | append (NonNil xs) (NonNil ys) = NonNil (Append (xs, ys))

更难的一件事是,如果你想在' alist 前面添加一个元素,即一个带有签名'a * 'a alist -> 'a alist的函数:

fun cons (x, Nil) = NonNil (...)
  | cons (x, NonNil (Sing y)) = NonNil (...)
  | cons (x, NonNil (Append (ys, zs))) = NonNil (...)

在每种情况下x都是前置的。有三种情况涉及到您要添加x的列表:列表为空,列表为非空且包含单个元素,或者列表为非空且包含另外两个列表的 Append 。在每种情况下,结果都是NonNil,因为将x添加到列表中永远不会给Nil

前两种情况应该是直截了当的。第三种情况,您必须考虑将x放在子列表yszs方面的位置。

像这样,您可以通过在REPL中键入open List;来构建所有找到的辅助功能。即使hdtl也不是完全无足轻重的,因为他们倾向于找到第一个元素和列表其余部分之间的分割。用于测试目的的有用功能是toList,签名为'a alist -> 'a list。为这些追加清单制作的有趣内容是rev。 : - )

由于您可能不会制作foldl

fun foldl f e Nil = e
  | foldl f e (NonNil (Sing x)) = f (x, e)
  | foldl f e (NonNil (Append (xs, ys))) =
      foldl f (foldl f e (NonNil xs)) (NonNil ys)

为了娱乐,您可以使用hd实施foldl并抛出异常:

fun hd xs =
    let exception FoundIt of 'a
    in foldl (fn (x, _) => raise FoundIt x) (fn _ => raise Empty) xs ()
       handle FoundIt x => x
    end

这里有一个稍微相关的StackOverflow帖子:Standard ML functor examples