OCaml-Exception:控制执行流程以减少内存使用量

时间:2013-10-08 15:02:17

标签: exception-handling ocaml

    let rec remove x = function
        y :: l when x = y -> l
       |y :: l (* x <> y *) -> y :: remove x l
       |[] -> []

这本书说这个函数有一个问题:当找不到元素时,整个列表被不必要地复制。因此,给出以下改进版本。

    exception Unchanged

    let rec remove_inner x = function
        y :: l when x = y ->
            l
       |y :: l ->
            y :: remove_inner x l
       |[] -> 
        raise Unchanged

    let remove x l =
        try remove_inner x l with
            Unchanged ->
                l

我不太明白这一点。

2 个答案:

答案 0 :(得分:1)

这基本上只是一个玩具示例,以说明您可能想要使用异常的原因,因此无需深入了解它。

要理解这一点,您只需要意识到列表会占用空间。因此,列表的两个副本将占用单个列表的两倍空间。但是在像OCaml这样的函数式语言中,列表是不可变的。由于无法修改列表,因此列表与列表副本之间没有可检测的差异。因此,如果您在两个地方使用相同的列表而不是实际复制列表,则可以在不改变程序含义的情况下节省空间。

当您被要求删除不存在的元素时,您显示的代码会执行此操作。结果将与输入相同,并且代码确保结果与输入(不是副本)相同。

请注意,节省空间来自避免复制,而不是来自异常本身。但是当列表不太长时,这段代码很好而且很紧凑。

我希望这会有所帮助。

(侧节点:在OCaml中,您可以使用==运算符实际检测两个列表之间的差异。这就是人们经常避免使用此运算符的原因;它会损害语言的功能纯度。您肯定想要要小心。)

答案 1 :(得分:0)

表达式y :: remove x l是复制的位置。那是因为::是列表元素的内置[中缀运算符]构造函数。

remove函数的幼稚版本中,::运算符用于构造一个新列表,其中包含原始列表的所有元素,但第一个元素等同于{{1}参数。如果原始列表没有这样的元素,那么它仍然会创建新列表,它将是一个等同于原始列表的副本。

x函数的“改进”版本调用另一个函数remove,当它到达原始列表的末尾而不删除元素时会引发remove_inner异常。如果外部Unchanged函数捕获remove异常,则返回原始列表。