阅读由专家撰写的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对foreach
的Lists
定义: 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。更多信息here和here。
关于scala - 在Scala中避免while循环有什么好处吗?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/18674743/