我具有以下递归函数
trait SequenceGenerator[T] {
def program(l: List[T])(implicit rule: ProductionRule[T]): List[T] = {
l.flatMap(rule.generate)
}
def sequenceNumber(seed: List[T], number: Int)(implicit rule: ProductionRule[T]): List[T] = {
number match {
case 1 => program(seed)
case a => program(sequenceNumber(seed, a - 1))
}
}
}
我想不出一种使sequenceNumber
尾递归的方法。
答案 0 :(得分:6)
将方法转换为tailrec的想法是,将每个下一个计算步骤放入累加器中,然后将该累加器传递回该方法,以便在下一步中,您可以返回累加器,或者根据新修改的累加器再次调用该方法您的步骤。
所以您会得到这样的东西:
trait SequenceGenerator[T] {
def program(l: List[T])(implicit rule: ProductionRule[T]): List[T] = l.flatMap(rule.generate)
def sequenceNumber(seed: List[T], number: Int)(implicit rule: ProductionRule[T]): List[T] = {
@tailrec
def tailRec(number: Int, acc: List[T]): List[T] = number match {
case 1 => acc
case a => tailRec(a-1,program(acc))
}
tailRec(number,program(seed))
}
}
但仅供参考,还有一种更先进的技术来建立尾部递归,称为Trampoling
。例如,可以使用scala.util.control.TailCalls
来实现。
我在这项技术上不是很好,但是看起来像这样:
import TailCalls._
trait SequenceGenerator[T] {
def program(l: TailRec[List[T]])(implicit rule: ProductionRule[T]):TailRec[List[T]] = {
l.map(_.flatMap(rule.generate))
}
def sequenceNumber(seed: TailRec[List[T]], number: TailRec[Int])(implicit rule: ProductionRule[T]): TailRec[List[T]] = {
number flatMap {
case 1 => tailcall(program(seed))
case a => tailcall(program(sequenceNumber(seed, done(a - 1))))
}
}
}
sequenceNumber
在此处不能标记为tailrec,但实际上在“背景”中为tailrec。要获得真实的计算结果,您需要致电sequenceNumber(done(...),done(...)).result
答案 1 :(得分:3)
也许所发布的代码过于简化,但看起来您实际上并不需要program()
或递归。
program(seed)
实际上只是seed.flatMap(rule.generate)
,因此递归展开到seed.flatMap(rule.generate).flatMap(rule.generate).flatMap(...
等,这就是iterate()
会为您做的事情。
def sequenceNumber(seed:List[T], number:Int)(implicit rule:ProductionRule[T]):List[T] =
Seq.iterate(seed.flatMap(rule.generate), number)(_.flatMap(rule.generate)).last