标准ML:回溯混乱

时间:2015-07-19 12:47:18

标签: functional-programming sml

我很困惑Harper在他的p简介中给出了一个例子。 110.他正在编写一个函数,从硬币值列表中进行一定程度的更改,并在需要时进行回溯。

e.g。如果我运行更改[5,2] 16,我得到[5,5,2,2,2](算法应尽可能贪婪)。

exception Change
fun change _ 0 = nil
    | change nil _ = raise Change
    | change (coin::coins) amt = 
        if coin > amt then
            change coins amt
        else
            (coin :: change (coin::coins) (amt - coin))
            handle Change => change coins amt

几个问题:

1)我对这个算法如何实现回溯感到有些困惑。看起来当使用硬币值的空列表作为其第一个参数调用更改时,它会引发更改异常。

但是Change异常处理程序调用{​​{1}}。这是如何解除最近贪婪的决定?

2)为什么处理程序放在else子句中?我原以为它完全是分开的......

感谢您的帮助, bclayman

1 个答案:

答案 0 :(得分:3)

这是呼叫change [5,2] 16的执行跟踪。左边的部分 handle表示函数到目前为止计算的内容,而部分 在右侧是通过Change信号请求回溯时继续的状态。

> change [5, 2] 16
> 5 :: change [5, 2] (16 - 5)                 handle Change: change [2] 16
> 5 :: change [5, 2] 11                       handle Change: change [2] 16
> 5 :: 5 :: change [5, 2] (11 - 5)            handle Change: 5 :: change [2] 11
> 5 :: 5 :: change [5, 2] 6                   handle Change: 5 :: change [2] 11
> 5 :: 5 :: 5 :: change [5, 2] (6 - 5)        handle Change: 5 :: 5 :: change [2] 6
> 5 :: 5 :: 5 :: change [5, 2] 1              handle Change: 5 :: 5 :: change [2] 6
> 5 :: 5 :: 5 :: change [2] 1
> 5 :: 5 :: 5 :: change nil 1
> raise Change => 5 :: 5 :: change [2] 6
> 5 :: 5 :: 2 :: change [2] (6 - 2)           handle Change
> 5 :: 5 :: 2 :: change [2] 4                 handle Change
> 5 :: 5 :: 2 :: 2 :: change [2] (4 - 2)      handle Change
> 5 :: 5 :: 2 :: 2 :: change [2] 2            handle Change
> 5 :: 5 :: 2 :: 2 :: 2 :: change [2] (2 - 2) handle Change
> 5 :: 5 :: 2 :: 2 :: 2 :: change [2] 0       handle Change
> 5 :: 5 :: 2 :: 2 :: 2 :: nil
> [5, 5, 2, 2, 2]

如您所见,当捕获更改异常时,算法会返回两个 堆栈帧,从结果列表中删除第三个5硬币并仅使用递归 硬币列表中的2枚硬币。金额也会重置为6。

第一行,handle之前的部分尝试使用另一个5作为可能 分解,而异常处理程序代表回溯选项, 即,删除我们刚尝试的5并调整硬币列表和剩余的 量。

最后一行表示最后安装的异常处理程序要回溯。

> 5 :: 5 :: 5 :: change [5, 2] 1              handle Change: 5 :: 5 :: change [2] 6
> 5 :: 5 :: 5 :: change [2] 1
> 5 :: 5 :: 5 :: change nil 1
> raise Change => 5 :: 5 :: change [2] 6

换句话说,算法在达到不再存在的状态时回溯 硬币类型可供选择,但数量仍然是积极的。它太贪心了 因为算法将使用相同的硬币,直到它捕获异常。

异常处理程序附加到else表达式,因为它在哪里 贪婪的选择。

希望我能理解我的解释。