【发布时间】:2021-05-05 18:37:56
【问题描述】:
这个简单的正则表达式实现 (scastie here) 无法编译,我期望它编译。错误出现在第 14 行,其中一个中间递归调用被解释为违反@tailrec 要求。虽然这个中间递归调用确实不在尾调用位置,但表达式的实际最后调用是,使得整个表达式的尾调用得到优化。
可以通过给中间递归调用起别名,将其转换为“任意”调用来说明这种推理方式。当 this 调用被 aliasMatches 别名时,代码按预期编译和执行。
为什么编译器不接受非别名实现?这是一个实际的限制,还是上面的推理有问题?有没有办法诱使编译器接受我缺少的第一个版本(除了完整的累加器重写)?
(使用 Scala 2.13.5)
import scala.annotation.tailrec
def matches(input: String, pattern: String): Boolean = {
@tailrec
def matchesRemainder(remainder: Seq[Char], remainingPattern: Seq[Char]): Boolean =
(remainder, remainingPattern) match {
case (Seq(), Seq()) => true
case (Seq(_@_*), Seq()) => false
case (Seq(), Seq(_, '*', _@_*)) => true
case (Seq(), Seq(_@_*)) => false
\\ vvvv error vvvv
case (Seq(_, rs@_*), Seq('.', '*', xs@_*)) => matchesRemainder(rs, remainingPattern) ||
matchesRemainder(remainder, xs)
case (Seq(r, rs@_*), Seq(p, '*', xs@_*)) => (r == p && aliasMatches(rs, remainingPattern)) ||
matchesRemainder(remainder, xs)
case (Seq(_, rs@_*), Seq('.', ps@_*)) => matchesRemainder(rs, ps)
case (Seq(r, rs@_*), Seq(p, ps@_*)) => r == p && matchesRemainder(rs, ps)
}
def aliasMatches(remainder: Seq[Char], p: Seq[Char]): Boolean =
matchesRemainder(remainder, p)
matchesRemainder(input, pattern)
}
【问题讨论】:
-
Scala 1.13.5 不存在
-
最后一个表达式不是递归调用而是对
||的调用 -
@LuisMiguelMejíaSuárez 那么为什么在使用别名时它会编译?如果第一个参数失败,
||不会评估它的最后一个参数,使最后一个参数成为尾调用? -
@cchantep darn,哎呀,你说得对,我显然忽略了我的错字(两次)。抱歉,我会改正的。
-
@JoostPapendorp 嗯,你是对的,但我不确定编译器是否足够聪明,可以解决这个问题;它可能是(它至少比我聪明)。 - 无论如何,如果你在同一个块中有两个递归调用,即使最后一个在尾部位置,第一个不是,这意味着你的代码确实会炸毁堆栈。
标签: scala tail-call-optimization