正如@JörgWMittag 所评论的,要处理带有尾递归的树,您必须转换计算,最直接的方法是模拟调用堆栈并将其传递给递归调用:
def combine[A](expression: Expression, runners: List[Runner[A]]): Runner[A] = {
val labelToRunner = runners.map(runner => (runner.label.get, runner)).toMap
sealed trait Element
case class Op(operation: Operation) extends Element
case class Expr(expression: Expression) extends Element
case class Result(runner: Runner[A]) extends Element
@tailrec
def reduce(stack: List[Element]): Runner[A] = {
def expand(expression: Expression): List[Element] = expression match {
case Label(_, value) =>
List(Result(labelToRunner(value)))
case Tree(_, operation, left, right)=>
List(Expr(left), Expr(right), Op(operation))
}
stack match {
case List(Result(runner)) => runner
case Expr(expression) :: rest =>
reduce(expand(expression) ::: rest)
case (left @ Result(_)) :: Expr(expression) :: rest =>
// The subtree we are processing is put on the top of the stack
// Thus when the operation is applied the elements are in reverse order
reduce(expand(expression) ::: left :: rest)
case Result(right) :: Result(left) :: Op(operation) :: rest =>
val combined = operation match {
case AND => left.and(right)
case OR => left.or(right)
}
reduce(Result(combined) :: rest)
}
}
reduce(List(Expr(expression)))
}
打印出该函数的踪迹以了解它如何首先扩展表达式、将简单表达式转换为结果以及应用操作是很有趣的。例如,这里是带有数字标签的模拟跑步者上的某些表达式的堆栈:
Expr(((1 AND (2 OR 3)) OR 4) AND ((5 OR 6) AND 7))
Expr((1 AND (2 OR 3)) OR 4), Expr((5 OR 6) AND 7), Op(AND)
Expr(1 AND (2 OR 3)), Expr(4), Op(OR), Expr((5 OR 6) AND 7), Op(AND)
Expr(1), Expr(2 OR 3), Op(AND), Expr(4), Op(OR), Expr((5 OR 6) AND 7), Op(AND)
Result(1), Expr(2 OR 3), Op(AND), Expr(4), Op(OR), Expr((5 OR 6) AND 7), Op(AND)
Expr(2), Expr(3), Op(OR), Result(1), Op(AND), Expr(4), Op(OR), Expr((5 OR 6) AND 7), Op(AND)
Result(2), Expr(3), Op(OR), Result(1), Op(AND), Expr(4), Op(OR), Expr((5 OR 6) AND 7), Op(AND)
Result(3), Result(2), Op(OR), Result(1), Op(AND), Expr(4), Op(OR), Expr((5 OR 6) AND 7), Op(AND)
Result(2 OR 3), Result(1), Op(AND), Expr(4), Op(OR), Expr((5 OR 6) AND 7), Op(AND)
Result(1 AND (2 OR 3)), Expr(4), Op(OR), Expr((5 OR 6) AND 7), Op(AND)
Result(4), Result(1 AND (2 OR 3)), Op(OR), Expr((5 OR 6) AND 7), Op(AND)
Result((1 AND (2 OR 3)) OR 4), Expr((5 OR 6) AND 7), Op(AND)
Expr(5 OR 6), Expr(7), Op(AND), Result((1 AND (2 OR 3)) OR 4), Op(AND)
Expr(5), Expr(6), Op(OR), Expr(7), Op(AND), Result((1 AND (2 OR 3)) OR 4), Op(AND)
Result(5), Expr(6), Op(OR), Expr(7), Op(AND), Result((1 AND (2 OR 3)) OR 4), Op(AND)
Result(6), Result(5), Op(OR), Expr(7), Op(AND), Result((1 AND (2 OR 3)) OR 4), Op(AND)
Result(5 OR 6), Expr(7), Op(AND), Result((1 AND (2 OR 3)) OR 4), Op(AND)
Result(7), Result(5 OR 6), Op(AND), Result((1 AND (2 OR 3)) OR 4), Op(AND)
Result((5 OR 6) AND 7), Result((1 AND (2 OR 3)) OR 4), Op(AND)
Result(((1 AND (2 OR 3)) OR 4) AND ((5 OR 6) AND 7))