阅读由专家撰写的Scala文档,您可能会感到,尾部递归比while循环更好,即使后者更简洁明了。这是一个例子

object Helpers {
    implicit class IntWithTimes(val pip:Int) {
            // Recursive
        def times(f: => Unit):Unit = {
            @tailrec
            def loop(counter:Int):Unit = {
                if (counter >0) { f; loop(counter-1) }
            }
            loop(pip)
        }

            // Explicit loop
        def :@(f: => Unit) = {
            var lc = pip
            while (lc > 0) { f; lc -= 1 }
        }
    }
}


(要明确一点,专家根本没有解决循环问题,但是在示例中,他们选择以一种本能的方式编写循环,这对我提出了一个问题:我是否应该发展类似的本能。 )

while循环中唯一可以改善的方面是迭代变量应位于循环主体本地,并且变量的突变应位于固定位置,但是Scala选择不提供该语法。

清晰度是主观的,但问题是(tail)递归样式是否提供改进的性能?

最佳答案

我很确定,由于JVM的限制,并不是所有潜在的尾递归函数都会被Scala编译器这样优化,因此对性能问题的简短回答(有时是错误的回答)不是。

对于更笼统的问题(具有优势)的长答案更加人为。请注意,使用while实际上是:


创建一个包含计数器的新变量。
对该变量进行突变。


一次性错误和可变性的危险将确保从长远来看,您将引入while模式的错误。实际上,您的times函数可以轻松实现为:

def times(f: => Unit) = (1 to pip) foreach f


这不仅更简单,更小,而且避免了任何瞬态变量和可变性的产生。实际上,如果您要调用的函数的类型与结果有关,那么while构造将变得更加难以阅读。请尝试仅使用whiles来实现以下内容:

def replicate(l: List[Int])(times: Int) = l.flatMap(x => List.fill(times)(x))


然后继续定义执行相同功能的尾递归函数。



更新:

我听到你说:“嘿!这是作弊!foreach既不是while也不是tail-rec通话”。真的吗?看一下Scala对foreachLists定义:

  def foreach[B](f: A => B) {
    var these = this
    while (!these.isEmpty) {
      f(these.head)
      these = these.tail
    }
  }


如果要了解有关Scala中的递归的更多信息,请查看this blog post。进入函数式编程后,请疯狂阅读Rúnar的blog post。更多信息herehere

关于scala - 在Scala中避免while循环有什么好处吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/18674743/

10-11 22:26
查看更多