Scala树递归折叠方法

标签 scala tree tail-recursion

给定(非二叉)树的以下定义:

sealed trait Tree[+A]
case class Node[A](value: A, children: List[Node[A]]) extends Tree[A]
object Tree {...}

我编写了以下fold方法:

def fold[A, B](t: Node[A])(f: A ⇒ B)(g: (B, List[B]) ⇒ B): B =
  g(f(t.value), t.children map (fold(_)(f)(g)))

可以很好地用于(除其他外)这个ma​​p方法:

def map[A, B](t: Node[A])(f: A ⇒ B): Node[B] =
  fold(t)(x ⇒ Node(f(x), List()))((x, y) ⇒ Node(x.value, y))

问题:有人可以帮助我如何编写上述折叠的尾递归版本吗?

最佳答案

我相信你需要一个堆栈来完成这样的遍历,就像在命令式编程中一样,如果没有递归方法,就没有自然的方式来编写它。当然,您始终可以自己管理堆栈,将其移动到堆中,并防止堆栈溢出。这是一个例子:

sealed trait Tree[+A]
case class Node[+A](value: A, children: List[Node[A]]) extends Tree[A]

case class StackFrame[+A,+B](
  value: A, 
  computedChildren: List[B], 
  remainingChildren: List[Node[A]])

def fold[A,B](t: Node[A])(f: A => B)(g: (B, List[B]) => B) : B = {

  def go(stack: List[StackFrame[A,B]]) : B = stack match {
    case StackFrame(v, cs, Nil) :: tail => 
      val folded = g(f(v), cs.reverse)
      tail match {
        case Nil => folded
        case StackFrame(vUp, csUp, remUp) :: rest => 
          go(StackFrame(vUp, folded::csUp, remUp)::rest)
      }
    case StackFrame(v, cs, nextChild :: others) :: tail =>
      go(
        StackFrame(nextChild.value, Nil, nextChild.children) ::
        StackFrame(v, cs, others) :: 
        tail)
    case Nil => sys.error("Should not go there")
  }

  go(StackFrame(t.value, Nil,  t.children) :: Nil)    
}

注意:我将 Node 设为协变,这并不是绝对必要的,但如果不是,则需要显式地指定一些 Nil 的类型(例如替换通过 List[X]()) 在某个地方。

go 它显然是尾递归,但只是因为它管理堆栈本身。

您可能会在this nice blog post中找到一种基于延续和蹦床的更有原则性和系统性的技术(但一开始并不容易掌握)。 。

关于Scala树递归折叠方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28868771/

相关文章:

gcc - gcc/g++ 中的尾递归

scala - 如何转换 ackermann 函数的变体以支持尾调用?

c - 告诉 GCC 尾调用一个函数

scala - spark如何将数据加载到内存中

scala - 有没有办法从 Scala 中的实例中删除特征?

java - 使用继承构建通用树

java - 当您必须将节点添加到末尾(二叉树构造)时,如何继续移动根?

java - 从 Java 转到 Scala,我应该使用 List 还是 Buffer 来替代 ArrayList

java - Scala Singleton 与 Java 的比较

python - 如何合并嵌套元组