对于普通列表,
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 可以做到这一点。要制作一个适用于 'a alist 的版本,您必须针对 'a alist 的不同情况进行模式匹配;只有当两个列表都是 NonNil 时,您才能使用 Append(因为空列表不能表示为 'a alistNN。任何一个操作数都是 @987654331 的情况@可以单独处理;
fun append Nil ys = ys
| append xs Nil = xs
| append (NonNil xs) (NonNil ys) = NonNil (Append (xs, ys))
变得更加困难的一件事是,如果您想在 'a 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。
前两种情况应该是直截了当的。第三种情况你必须考虑根据子列表ys 和zs 将x 放在哪里。
像这样,您可以通过在 REPL 中键入 open List; 来构建所有辅助功能。即使hd 和tl 也不是完全无关紧要的,因为它们一心想要找到第一个元素和列表其余部分之间的分割点。用于测试目的的有用函数是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)
为了娱乐,您可以使用foldl 实现hd 并抛出异常:
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