【问题标题】:Tail Recursive Factorial Function in ScalaScala中的尾递归阶乘函数
【发布时间】:2021-01-25 07:02:42
【问题描述】:

我认为下面的阶乘函数是尾递归的,当我测试它时,它可以正常工作到 10 并且在 20 时变得奇怪(负输出),当我插入 100 时,答案是 0:

def factorial(n: Int, m: Int = 1): Int = 
    if (n == 0) m else fact(n-1, m * n)

但是当我把 @tailrec 放在它上面时,我得到以下错误:

error: not found: type tailrec

我不明白为什么这个函数不是尾递归的。堆栈递归阶乘函数是:

def factorial(n: Int): Int = 
    if (n == 0) 1 else n * factorial(n-1)

上述函数在每次递归调用时修改外部表达式after else。而第一个函数只修改函数内部的内容。现在,要创建递归阶乘函数,他们所做的就是在函数内部创建一个函数。但是,能否仅使用本题中第一个函数的主体来创建递归阶乘函数?

另外,前一个函数中的“m”是变量吗?

编辑:现在按照answer 中的建议进行操作后, 如果函数不是尾递归的,我会收到错误消息:

error: could not optimize @tailrec annotated method factorial: it contains a recursive call not in tail position

【问题讨论】:

  • tailrec 不会改变函数的结果,它会因为整数溢出而得到负数或0
  • 错误消息中告诉您未找到类型tailrec 的内容让您认为该方法不是尾递归的?
  • @JörgWMittag 这里是初学者。 AFAIK,tailrec 检查函数是否是尾递归的,如果不是,则显示错误。我搜索了在这种情况下显示的确切错误,我找不到它。从错误中我假设编译器无法确定函数的“类型”为 tailrec 或无法将其归类为一个。编程中有很多术语我没有深入理解。

标签: scala recursion tail-recursion


【解决方案1】:

几件事:

  • 您必须导入@tailrec 注解才能在没有全名的情况下使用它:

    import scala.annotation.tailrec
    
    @tailrec
    def factorial(n: Int, m: Int = 1): Int = 
      if (n == 0) m else fact(n-1, m * n)
    

    没有 @tailrec scalac 仍然可以进行尾递归优化,只是不会强制执行(如果不可能进行 TRO,它不会导致编译失败)。

  • 整数的容量有限 - 它是 32 位 2-compliment,大于 2^31-1 的所有内容都会溢出并变为负数

因此您必须导入注释或使用全名 (@scala.annotation.tailrec) 并将 Int 替换为更大的名称(Long 也不够,更像BigInteger)。

【讨论】:

  • “我现在可以安全地假设该函数在测试后是尾递归的”。您无需进行测试即可安全地假设这一点。如果你在函数上加上@tailrec注解,编译器不拒绝它,就保证是尾递归的。 (这就是注释的重点)
猜你喜欢
  • 2018-06-08
  • 2021-03-18
  • 2019-08-06
  • 1970-01-01
  • 2023-03-25
  • 2015-04-16
  • 1970-01-01
  • 1970-01-01
  • 2018-05-16
相关资源
最近更新 更多