【问题标题】:Backtracking in Standard ML标准 ML 中的回溯
【发布时间】:2013-07-27 21:24:07
【问题描述】:

我在我的 SML 手册中看到了以下函数,它计算特定种类的硬币需要多少特定种类的硬币才能进行特定的更改。 例如change [5,2] 16 =[5,5,2,2,2],因为有两个 5 硬币和三个 2 硬币,一个得到 16。

以下代码为回溯方式:

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

handle Change=> change coins amt;

它有效,但我不明白具体如何。 我知道回溯是什么,我只是不明白这个特殊的功能。

到目前为止我的理解是:如果amt 为 0,则表示我们的更改已计算出来,并且没有任何内容可以添加到最终列表中。

如果我们的“coin-list”中没有更多的硬币,我们需要后退一步。

这就是我迷失的地方:引发异常究竟如何帮助我们返回?

在我看来,处理程序尝试调用change 函数,但“coins”参数不应该是nil 吗?因此进入无限循环?为什么会“回去”?

最后一个条款对我来说很明显:如果硬币价值大于剩余的找零数量,我们使用 剩余 硬币来构建找零。如果它小于剩余的数量,我们将其添加到结果列表中。

【问题讨论】:

    标签: standards sml backtracking ml coin-change


    【解决方案1】:

    最好通过写出一个简单示例的评估过程来了解这一点。在每个步骤中,我只需将调用 change 替换为相应的右侧(为了更加清晰,我添加了额外的括号):

      change [3, 2] 4
    = if 3 > 4 then ... else ((3 :: change [3, 2] (4 - 3)) handle Change => change [2] 4)
    = (3 :: change [3, 2] 1) handle Change => change [2] 4
    = (3 :: (if 3 > 1 then change [2] 1 else ...)) handle Change => change [2] 4
    = (3 :: change [2] 1) handle Change => change [2] 4
    = (3 :: (if 2 > 1 then change [] 1 else ...)) handle Change => change [2] 4
    = (3 :: (raise Change)) handle Change => change [2] 4
    

    此时已引发异常。它冒泡到当前处理程序,以便评估按如下方式进行:

    = change [2] 4
    = if 2 > 4 then ... else ((2 :: change [2] (4 - 2)) handle Change => change [] 4)
    = (2 :: change [2] 2) handle Change => change [] 4
    = (2 :: (if 2 > 2 then ... else ((2 :: change [2] (2 - 2)) handle Change => change [] 2)) handle Change => change [] 4
    = (2 :: ((2 :: change [2] 0) handle Change => change [] 2)) handle Change => change [] 4
    = (2 :: ((2 :: []) handle Change => change [] 2)) handle Change => change [] 4
    = (2 :: (2 :: [])) handle Change => change [] 4
    = 2 :: 2 :: []
    

    到这里为止没有更多的失败,所以我们成功终止了。

    简而言之,每个处理程序都是一个回溯点。在每次失败(即引发)时,您都会在最里面的处理程序处继续,这是最后一个回溯点。每个处理程序本身都设置为包含相应的 try 调用。

    【讨论】:

      【解决方案2】:

      来源https://www.cs.cmu.edu/~rwh/introsml/core/exceptions.htm

      表达式 exp handle match 是一个异常处理程序。它是 通过尝试评估 exp 来评估。如果它返回一个值,那么 那是整个表达式的值;处理程序没有任何作用 在这种情况下。但是,如果 exp 引发异常 exn,则 异常值与匹配的子句匹配(确切地说 就像在一个论点上应用一个从句函数) 确定如何进行。如果子句的模式与 异常exn,然后用表达式部分继续求值 该条款。如果没有匹配的模式,异常 exnre-raised 所以 外部异常处理程序可能会对其进行调度。如果没有处理程序 处理异常,然后将未捕获的异常表示为 评估的最终结果。也就是说,计算被中止 未捕获的异常exn

      在更操作方面,exp handle match 的评估通过以下方式进行 安装由 match 确定的异常处理程序,然后评估 exp。异常处理程序的先前绑定被保留,因此 一旦不再需要给定的处理程序,它就可以恢复。 引发异常包括将 exn 类型的值传递给 当前异常处理程序。将异常传递给处理程序 卸载该处理程序,并重新安装以前活动的 处理程序。这确保了如果处理程序本身引发异常, 或未能处理给定的异常,则异常为 在评估 handle 之前传播到活动处理程序 表达。如果表达式没有引发异常,则 作为完成评估的一部分,之前的处理程序被恢复 handle 表达式。

      【讨论】:

        【解决方案3】:

        您可以将这种异常用法改写为使用'a option 类型。原函数:

        exception Change;
        fun change _ 0 = []
          | change [] _ = 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;
        

        在下面修改后的函数中,异常没有冒泡,而是变成了NONE。在这里变得更加明显的一件事是,coin 仅出现在这两种情况之一(在上面的代码中,它总是出现,但在回溯的情况下会恢复)。

        fun change' _ 0 = SOME []
          | change' [] _ = NONE
          | change' (coin::coins) amt =
            if coin > amt
            then change' coins amt
            else case change' (coin::coins) (amt-coin) of
                     SOME result => SOME (coin :: result)
                   | NONE        => change' coins amt
        

        另一种演示发生情况的方法是绘制调用树。这并没有像 Andreas Rossberg 的手工评估那样收集结果,但它确实表明只有 change 采用 else-branch 的时间才有可能回溯,如果发生回溯(即 NONE 是返回或抛出异常),请不要在结果中包含coin

          (original call ->)  change [2,5] 7
                              \ (else)
                               `-change [2,5] 5
                              /  \ (else)
          ___________________/    `-change [2,5] 3
         /                       /  \ (else)
        /                       /    `-change [2,5] 1
        `-change [5] 5         /       \ (then)
          \ (else)            /         `-change [5] 1
           `-change [] 0     /            \ (then)
             \              /              `-change [] 1
              `-SOME []     `-change [5] 3   \ (base)
                             \ (then)         `-NONE
                              `-change [] 3
                                \
                                 `-NONE
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-04-05
          • 2011-04-07
          • 2010-11-12
          • 2011-05-05
          • 2011-01-03
          • 1970-01-01
          • 2013-01-02
          • 2013-01-01
          相关资源
          最近更新 更多