【发布时间】:2018-01-31 02:35:24
【问题描述】:
我想了解,这些尾递归将如何在后台执行,我最近在一个用 scala 编写的大数据分配中看到。我还提供了两个附加版本的实现,我更愿意如何编写这段代码 - 其中一个也是尾递归的,另一个 - 不是;但是,我想了解第一个实现如何提供相同的结果。
所以,这里是(目的是在列表(ls)中找到特定名称(标签)的索引):
def firstLangInTag(tag: Option[String], ls: List[String]): Option[Int] = {
if (tag.isEmpty) None
else if (ls.isEmpty) None
else if (tag.get == ls.head) Some(0)
else {
val tmp = firstLangInTag(tag, ls.tail)
tmp match {
case None => None
case Some(i) => Some(i + 1)
}
}
}
当我们考虑执行时,将看到参数'tag'定义为Option(“Scala”),而'ls'定义为List(“Java”,“PHP”,“Scala”):
- val tmp = firstLangInTag(Scala, (PHP, Scala)) => 返回 Some(2)
- val tmp = firstLangInTag(Scala, (Scala)) => 返回 Some(1)
所以我们有一个答案 Some(2) 并且它是正确的,但是有人可以解释一下,在执行过程中 var 'i'(隐含的 var 'tmp')保存在哪里。是因为尾递归为每次递归执行提供了一个堆栈,而“i”只是保存在内存中并且每次迭代期间都会更新?为什么 var 'tmp' 不仅会被每次迭代覆盖,而且结果会累积(+ 1)。如果您查看以下使用 'accumulator' 的实现,但再次递归,那么很明显,结果已存储在名为 'acc' 的变量中,因此 'acc' 返回此函数的结果:
def firstLangInTag(tag: Option[String], ls: List[String]): Option[Int] = {
def counter(acc: Int, tag: Option[String], ls: List[String]): Int = {
if (tag.isEmpty) acc
else if (ls.isEmpty) acc
else if (tag.get == ls.head) acc
else counter(acc + 1, tag, ls.tail)
}
Option(counter(0, tag, ls))
同样,我们也可以实现与非尾递归函数相同的结果(前提是它会返回 Int 而不是 Option):
...
else 1 + firstLangInTag(tag, ls.tail);
但是,请有人向我解释一下第一个函数以及 scala 如何将结果存储在 VAL 中并在每次下一次迭代时更新它;
提前谢谢你!
【问题讨论】:
标签: scala tail-recursion