Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/vue.js/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala 尾部递归:比较两种情况_Scala_Recursion_Functional Programming_Stack Overflow - Fatal编程技术网

Scala 尾部递归:比较两种情况

Scala 尾部递归:比较两种情况,scala,recursion,functional-programming,stack-overflow,Scala,Recursion,Functional Programming,Stack Overflow,为什么这是尾部递归: def navigate(myList : List[Int]) : (Int, List[Int]) = { def navigate(step: Int, offset: Int, myList: List[Int]): (Int, scala.List[Int]) = { if //some test and exit condition, then a definition of jump else navigate(step +

为什么这是尾部递归:

  def navigate(myList : List[Int]) : (Int, List[Int]) = {
    def navigate(step: Int, offset: Int, myList: List[Int]): (Int, scala.List[Int]) = {
      if //some test and exit condition, then a definition of jump
      else navigate(step + 1, offset + jump, myList)
    }
    navigate(0, 0, myList)
  }
虽然这不是:

def navigate(myList : List[Int]) : (Int, List[Int]) = {
  navigate(0, 0, myList)
}

def navigate(step: Int, offset: Int, myList: List[Int]): (Int, scala.List[Int]) = {
  if //some test and exit condition, then a definition of jump
  else navigate(step + 1, offset + jump, myList)
}
如果
myList
很长,则第一种情况不会出现任何问题,而第二种情况会导致
StackOverflowerError


还有,有没有办法告诉编译器应该编译后一种方法,以便递归不会增加堆栈?

为了使方法符合尾部递归优化的条件,它必须:

  • 是尾部递归的(duh!)
  • 不使用
    return
  • final
您的两个示例都符合#1和#2,但只有第一个示例符合#3(局部方法是隐式最终的)

如果一个方法不是
final
,那么它不是尾部递归的原因是“尾部递归”意味着“尾部调用自身”,但是如果该方法是虚拟的,那么您就无法知道它是尾部调用自身还是其重写版本。在编译时确定方法是否被重写需要进行类层次结构分析,这相当于解决停止问题…IOW是不可能的

还有,有没有办法告诉编译器应该编译后者,这样递归就不会增加堆栈

不,没有办法打开或关闭尾部递归优化。尾部递归的方法(当然,根据Scala语言规范对“尾部递归”的定义)总是经过优化的。任何不这样做的Scala实现都违反了Scala语言规范


但是,还有
scala.annotation.tailrec
注释,它保证如果使用此注释注释的方法不符合SLS的尾部递归定义,编译器将生成错误。

为了使方法符合尾部递归优化的条件,它必须:

  • 是尾部递归的(duh!)
  • 不使用
    return
  • final
您的两个示例都符合#1和#2,但只有第一个示例符合#3(局部方法是隐式最终的)

如果一个方法不是
final
,那么它不是尾部递归的原因是“尾部递归”意味着“尾部调用自身”,但是如果该方法是虚拟的,那么您就无法知道它是尾部调用自身还是其重写版本。在编译时确定方法是否被重写需要进行类层次结构分析,这相当于解决停止问题…IOW是不可能的

还有,有没有办法告诉编译器应该编译后者,这样递归就不会增加堆栈

不,没有办法打开或关闭尾部递归优化。尾部递归的方法(当然,根据Scala语言规范对“尾部递归”的定义)总是经过优化的。任何不这样做的Scala实现都违反了Scala语言规范


但是,还有
scala.annotation.tailrec
注释,它保证如果使用此注释注释的方法不符合SLS的尾部递归定义,编译器将生成错误。

可能有一些用处,我从中得到的是,你可以用
@tailrec
注释,如果它没有优化,你会得到一个编译器警告。另外,你可能会得到一个关于为什么它没有优化的详细通知。@ameer我已经试过了,但我得到了编译错误“用@tailrec”注释的方法不是私有的,也不是最终的(因此可以重写)“在我的情况下,这可能是好的,但一般来说可能不是……我猜第一个示例中的方法是私有的/最终的,因为范围(嵌入外部导航意味着它不会被修改)。您是否尝试过将第二个示例设置为私有/最终?将其设置为私有/最终意味着它不能像警告所说的那样被覆盖(例如,函数定义定义定义一次之后不能更改)。如果不是这样,则优化将无效,因为正在递归的函数的定义可能会更改,因此我们无法确保将递归函数调用更改为循环是安全的,并且函数调用需要保持不变。可能有一些用处,我从中得到的是您可以注释使用
@tailrec
时,如果未优化,您将收到一条编译器警告。此外,您可能会收到一条关于未优化原因的详细通知。@ameer我已经尝试过,但我得到了编译错误“用'@tailrec'注释的方法不是私有的,也不是最终的(因此可以重写)”在我的例子中,这可能是好的,但一般来说可能不是……我猜第一个示例中的方法是私有/最终的,因为范围(嵌入外部导航意味着它不会被修改)。您是否尝试过将第二个示例设置为私有/最终?将其设置为私有/最终意味着它不能像警告所说的那样被覆盖(例如,函数定义定义定义一次之后不能更改)。如果不是这样,则优化将无效,因为正在递归的函数的定义可能会更改,因此我们无法确保将递归函数调用更改为循环是安全的,并且函数调用需要保持不变。谢谢!“你不知道它是tail调用自身还是重写版本的自身”谢谢,这正是我错过的。谢谢!”你不知道它是tail调用自身还是重写版本的自身“谢谢,这正是我错过的。