在SML中引发/抛出异常

时间:2017-10-07 18:26:55

标签: function exception sml ml

写两个版本的函数power,第一个版本接受两个整数参数m和n 并返回值m ^ n

注意m和n都可以是任何整数(正数或非正数)。  该函数的第二个版本被称为(cpower m n),它是curried版本。

备注:如果m和n均为零,则应引发异常。

fun power(m,n) = if n=0 then 1 else m * power(m,n-1);

fun cpower (m,n) : int =
    if n = 0 then 1 else
    if n >= 0 then power(m,n) else
    if n < 0 then 1/power(m,n) else m * cpower (m,n-1);

如何添加以便此函数抛出异常?

1 个答案:

答案 0 :(得分:1)

  1. 您的cpower函数没有正确的类型签名。它没有咖喱。带有两个参数的curried函数如下所示:

    fun cpower m n = ...
    

    它的类型为 int→int→int ,而不是power的类型(int×int)→int 。具有两个参数的Curried函数等效于具有一个参数的函数,该参数返回具有另一个参数的函数。 E.g。

    fun cpower m = (fn n => ...)
    

    也可以写

    val rec cpower = (fn m => fn n => ...)
    
  2. 您当前的cpower函数似乎有n<0的特殊情况,但您在这里写1/power(m,n)。但是/运算符没有为你的程序的其余部分通过整数文字(0,1)假设的整数定义,如果结果是小于1的分数,则它不在整数域中。

  3. 在这两种情况下,请考虑使用模式匹配而不是if-then-else。对于第一个电源功能,它看起来像:

    fun naive_power (m, 0) = 1
      | naive_power (m, n) = m * naive_power (m, n-1)
    
  4. 当m和n均为零时,您的函数不会抛出。 (我的版本也没有使用模式匹配。)如果m和n都为零,你可能想要写一个额外的if-then-else和throw,例如等,

    fun power (m, 0) = 1
      | power (m, n) = if n < 0 then raise Domain else m * power (m, n-1)
    

    关于这个函数的一个坏处是,它会在每次递归调用时检查n < 0是否真的,你知道如果第一次它是正数而基本情况会在0时捕获它,在任何后期都不会是负面的。这里一个优雅的解决方案是将函数的递归部分包装在一个非递归函数中,该函数执行一次一次,例如等,

    fun power (0, 0) = raise Domain
      | power (m, n) = if n < 0 then raise Domain else naive_power (m, n)
    

    其中naive_power是上面假定其输入有效的函数。

  5. 关于这个函数的另一个坏处是它很容易就不是尾递归的。也就是说,对power (m, 5)的调用将评估为:

    power (2, 5) ~> 2 * (power (m, 4))
                 ~> 2 * (2 * (power (m, 3)))
                 ~> 2 * (2 * (2 * (power (m, 2))))
                 ~> 2 * (2 * (2 * (2 * (power (m, 1)))))
                 ~> 2 * (2 * (2 * (2 * (2 * power (m, 0)))))
                 ~> 2 * (2 * (2 * (2 * (2 * 1))))
                 ~> 2 * (2 * (2 * (2 * 2)))
                 ~> 2 * (2 * (2 * 4))
                 ~> 2 * (2 * 8)
                 ~> 2 * 16
                 ~> 32
    

    意味着很多函数调用等待下一个函数调用在自身解析之前解析。尾递归版本可能使用附加参数来存储临时结果并在结尾返回它:

    fun power (0, 0) = raise Domain
      | power (M, N) =
        let fun power_helper (m, 0, result) = result
              | power_helper (m, n, tmp) = power_helper (m, n-1, tmp * m)
        in if N < 0 then raise Domain else power_helper (M, N, 1) end
    

    将辅助函数嵌入到其他函数中会很有用,因为您需要执行一次某些检查并在另一个函数中解析算法的主要递归部分,或者因为您希望向递归函数添加更多参数没有打破类型签名。 (power_helper有三个参数,所以尾部递归版本不会被包装成是一个有效的解决方案,可以解决用两个计算mⁿ的参数编写函数的问题。 p>

    评估power (2, 5),假设它的尾部重用实现看起来像这样:

    power (2, 5) ~> power_helper (2, 5, 1)
                 ~> power_helper (2, 4, 2)
                 ~> power_helper (2, 3, 4)
                 ~> power_helper (2, 2, 8)
                 ~> power_helper (2, 1, 16)
                 ~> power_helper (2, 0, 32)
                 ~> 32