【问题标题】:Raise/throw exception in SML在 SML 中引发/抛出异常
【发布时间】:2018-03-19 07:38:04
【问题描述】:

写两个版本的函数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);

如何添加才能让这个函数抛出异常?

【问题讨论】:

    标签: function exception sml ml


    【解决方案1】:
    1. 您的cpower 函数没有正确的类型签名。它不是咖喱。带有两个参数的柯里化函数如下所示:

      fun cpower m n = ...
      

      它的类型是 int → int → int,而不是 power 的类型 (int × int) → int。带有两个参数的柯里化函数等效于带有一个参数的函数返回带有另一个参数的函数。例如

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

      也可以写

      val rec cpower = (fn m => fn n => ...)
      
    2. 您当前的cpower 函数似乎有n&lt;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 并抛出,例如喜欢,

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

      这个函数的一个坏处是它会检查n &lt; 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ⁿ 的函数的有效解决方案。

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

    【讨论】:

      猜你喜欢
      • 2011-01-04
      • 1970-01-01
      • 2010-09-08
      • 2012-01-24
      • 2013-05-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多