Scala树递归折叠方法
Scala tree recursive fold method
给定(非二叉)树的以下定义:
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)))
可以很好地用于(除其他外)此 map 方法:
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))
问题:谁能帮我写一个尾递归版本的上述fold?
我相信你需要一个堆栈来进行这样的遍历,就像在命令式编程中一样,如果没有递归方法,就没有自然的方式来编写它。当然,您始终可以自己管理堆栈,将其移入堆中,并防止堆栈溢出。这是一个例子:
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 中找到基于延续和蹦床的更有原则和系统的技术(但一开始不容易掌握)。
给定(非二叉)树的以下定义:
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)))
可以很好地用于(除其他外)此 map 方法:
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))
问题:谁能帮我写一个尾递归版本的上述fold?
我相信你需要一个堆栈来进行这样的遍历,就像在命令式编程中一样,如果没有递归方法,就没有自然的方式来编写它。当然,您始终可以自己管理堆栈,将其移入堆中,并防止堆栈溢出。这是一个例子:
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 中找到基于延续和蹦床的更有原则和系统的技术(但一开始不容易掌握)。