Raise/throw SML 异常

Raise/throw exception in SML

编写函数 power 的两个版本,第一个版本有两个整数参数 m 和 n 和 returns 值 m^n

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

备注:如果 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. 您的 cpower 函数没有正确的类型签名。它不是咖喱。带有两个参数的柯里化函数如下所示:

    fun cpower m n = ...
    

    它的类型是 int → int → int 而不是 power 的类型 (int × int) → int.具有两个参数的 Curried 函数等同于具有一个参数的函数 return 具有另一个参数的函数。例如

    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 都为零时,您的函数不会抛出异常。 (我的版本也没有使用模式匹配。)你可能想写一个额外的 if-then-else 并在 m 和 n 都为零时抛出,例如喜欢,

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

    这个函数的一个坏处是它会在每次递归调用时检查是否 n < 0 ,实际上,你知道如果它第一次是肯定的并且基本情况会捕捉到它在 0 时,它在以后的任何阶段都不会是负数。这里一个优雅的解决方案是将函数的递归部分包装在执行这些检查 once 的非递归函数中,例如喜欢,

    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
    

    意味着很多函数调用在等待下一个函数调用解决之前才能解决。尾递归版本可能会使用一个额外的参数来存储临时结果并 return 最后:

    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ⁿ 的两个参数的函数问题的有效解决方案.

    评估 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