我正在尝试在Scala中实现typesafe perfect binary tree。换句话说,应该编译以下内容:
Succ(Node(Leaf("a"), Leaf("b")))
Node(
Succ(Node(Leaf("a"), Leaf("b"))),
Succ(Node(Leaf("c"), Leaf("d"))))
但以下不应该:
Node(
Succ(Node(Leaf("a"), Leaf("b"))),
Leaf("c"))
我提出了满足上述要求的解决方案,但是可以欺骗编译器:
Node(
Leaf("f"): PerfectBinaryTree,
Succ(Node(Leaf("a"), Leaf("b"))): PerfectBinaryTree)
Scala有没有办法避免这种情况? 它与Haskell有什么不同(如果有的话)?
trait PerfectBinaryTree {
type N <: PerfectBinaryTree
}
case class Succ[P <: PerfectBinaryTree](p: P) extends PerfectBinaryTree {
type N = Succ[P]
}
class Leaf[T] private (t: T) extends PerfectBinaryTree {
type N = Leaf[T]
}
object Leaf {
def apply[T](t: T): Leaf[T] = new Leaf(t)
}
case class Node[A <: PerfectBinaryTree, B <: PerfectBinaryTree](l: A, r: B)(implicit evidence: A =:= B) extends PerfectBinaryTree {
type N = A
}
答案 0 :(得分:4)
技巧(就像在Haskell中一样)是在类型变量(polymorphic recursion)内传递Node
。
然后,类的定义变得非常简单
case class Node[+A](left: A, right: A);
sealed trait Tree[+A];
case class Succ[+A](subtree: Tree[Node[A]]) extends Tree[A];
case class Leaf[+A](value: A) extends Tree[A];
(当然你想要添加折叠/遍历这样的树等功能。)
然后,在创建值时,Succ
构造函数的数量决定了叶子需要多少Node
个。请注意,始终只有一个叶子,但它包含一个由给定数量的Node
级别组成的二叉树:
val sample: Tree[String] =
Succ(
Succ(
Leaf(
Node(
Node("a", "b"),
Node("c", "d")
)
)
)
);