Scala 无法优化@tailrec带注释的方法循环:它包含一个不在尾部位置的递归调用

Scala 无法优化@tailrec带注释的方法循环:它包含一个不在尾部位置的递归调用,scala,recursion,tail-recursion,Scala,Recursion,Tail Recursion,我有下面的递归函数,我想对它使用尾部递归。但编译器抱怨我的实现有以下错误:error:(79,7)无法优化@tailrec注释的方法循环:它包含一个不在尾部位置的递归调用 n匹配{ ^ 是因为for循环,它假设它不在尾部位置吗 def dsl[N,E](qNodes:QNodeLike[N,E]*) = { val markers = scala.collection.mutable.Map.empty[String, N] @tailrec def loop(n:QNo

我有下面的递归函数,我想对它使用尾部递归。但编译器抱怨我的实现有以下错误:
error:(79,7)无法优化@tailrec注释的方法循环:它包含一个不在尾部位置的递归调用
n匹配{
^

是因为for循环,它假设它不在尾部位置吗

def dsl[N,E](qNodes:QNodeLike[N,E]*) = {
    val markers = scala.collection.mutable.Map.empty[String, N]
    @tailrec
    def loop(n:QNodeLike[N,E]):Unit = {
      n match {
        case QNode(head, kids:Seq[HalfEdgeLike[E,N]]) => {

          for(kid <- kids){
            kid match {
              case EmptyHalfEdge() =>
              case HalfEdge(e, n) => loop(n)
            }
          }
        }

        case QNodeMarker(head, marker, kids:Seq[HalfEdgeLike[E,N]]) => {
          markers.update(marker,head)
          for(kid <- kids){
            kid match {
              case EmptyHalfEdge() =>
              case HalfEdge(e, n) => loop(n)
            }
          }
        }
      }
    }

    loop(qNodes.head)
  }
defdsl[N,E](qNodes:QNodeLike[N,E]*)={
val markers=scala.collection.mutable.Map.empty[String,N]
@泰勒克
def循环(n:QNodeLike[n,E]):单位={
n匹配{
案例QNode(头部、儿童:Seq[HalfEdgeLike[E,N]])=>{
为了(孩子)
外壳半边(e,n)=>环(n)
}
}
}
案例QNodeMarker(头部、标记器、儿童:Seq[HalfEdgeLike[E,N]])=>{
标记。更新(标记,头部)
为了(孩子)
外壳半边(e,n)=>环(n)
}
}
}
}
}
循环(qNodes.head)
}

是的,这是因为循环。tailrec函数的结果必须是递归调用的结果。在您的情况下,结果是for语句的结果。

是的,这是正确的。要使其尾部递归,应使用传递到递归中的显式累加器

然而,除非您有非常深和窄的树,否则您不太可能需要尾部递归优化,因为在您最终导致堆栈溢出之前,运行时将增长非常大

以下是如何使其尾部优化的大致思路:

@tailrec
def loop(n:List[QNodeLike[N,E]]):Unit = {
  n match {
    case QNode(head, kids:Seq[HalfEdgeLike[E,N]]) :: rem => {
      kids match {
        case Nil =>
        case EmptyHalfEdge() :: rem2 => loop(rem2 ::: rem)
        case HalfEdge(e, n) :: rem2 => loop(n :: rem2 ::: rem)
      }
    }

    case QNodeMarker(head, marker, kids:Seq[HalfEdgeLike[E,N]]) :: rem => {
      markers.update(marker,head)
      kids match {
        case Nil =>
        case EmptyHalfEdge() :: rem2 => loop(rem2 ::: rem)
        case HalfEdge(e, n) :: rem2 => loop(n :: rem2 ::: rem)
      }
    }

    case Nil =>
  }
}

感谢您的回答,但关于您的解决方案:rem2的类型是Seq[HaldEdgeLike[N,E]]而不是SeqpQNodeLike[N,E]]啊..HalfEdgeLike不是QNodeLike的子类型?这让它有点混乱,但你可以应用相同的原则。只需传递两个参数
edgeLikeRem
nodeLikeRem
,并分别处理它们。或者,将两个递归级别展开到一个函数中,以便提取所有
QNodeLike
s在一个堆栈帧中QNode的所有
半边
,因此仅在累加器中传递
QNodeLike
s