如何在Scala中为树代数数据类型的叶子调用构造函数?
我正在创建一些基本的抽象数据类型和算法来复习我的CS基础知识,并在此过程中学习Scala。我的BinarySearchTree数据类型遇到了问题,它是一个更抽象的BinaryTree的实现:如何在Scala中为树代数数据类型的叶子调用构造函数?,scala,generics,constructor,abstract-data-type,algebraic-data-types,Scala,Generics,Constructor,Abstract Data Type,Algebraic Data Types,我正在创建一些基本的抽象数据类型和算法来复习我的CS基础知识,并在此过程中学习Scala。我的BinarySearchTree数据类型遇到了问题,它是一个更抽象的BinaryTree的实现: abstract class BinaryTree[T](stored_value: T) { var contents = stored_value var l: this.type = _ var r: this.type = _ ... } class BinarySearchTr
abstract class BinaryTree[T](stored_value: T) {
var contents = stored_value
var l: this.type = _
var r: this.type = _
...
}
class BinarySearchTree[T <: Ordered[T]](stored_value: T) extends BinaryTree(stored_value) {
def insert(newval: T) {
if (newval <= contents) {
if (l == null) {
throw new NotDefinedError("Still trying to work around type erasure.")
} else {
l.insert(newval)
}
} else {
if (r == null) {
throw new NotDefinedError("Still trying to work around type erasure.")
} else {
r.insert(newval)
}
}
}
或:
我是否需要在BinarySearchTree的定义中使用不同的类型覆盖l和r?或者在我给构造函数附加新值时调用不同类型的构造函数?或者其他选项?我会这样定义它:
class BinaryTree[T](
val item: T,
val left: Option[BinaryTree[T]] = None,
val right: Option[BinaryTree[T]] = None) {
override def toString() = "Tree(%s,%s,%s)".format(item,left,right)
}
class BinarySearchTree[T: Ordering](
override val item: T,
override val left: Option[BinarySearchTree[T]] = None,
override val right: Option[BinarySearchTree[T]] = None) extends BinaryTree[T](item, left, right) {
def insert(newval: T): BinarySearchTree[T] = {
val (newLeft, newRight) =
if (implicitly[Ordering[T]].lteq(newval, item))
(insertSubtree(newval, left), right)
else
(left, insertSubtree(newval, right))
new BinarySearchTree(item, newLeft, newRight)
}
private def insertSubtree(newval: T, subtree: Option[BinarySearchTree[T]]) =
Option(subtree
.map(_.insert(newval))
.getOrElse(new BinarySearchTree(newval, None, None)))
}
有一些基本的东西我已经改成更像Scala:
- 使用所有
字段使结构不可变。让val
返回一个新的修改过的树。尽可能多地保留旧(不可变)树以避免浪费内存insert
- 通过使用
选项来避免使用
。因此,null
和左
子树是右
,明确表示它们可能存在,也可能不存在选项[Binary(Search)Tree[T]]
- 使用子树上的
,因为它是一个映射
。选项
中的代码基本上是说“如果子树存在,请插入其中。否则,请获取一个新树。”insertSubtree
scala> var t = new BinarySearchTree(5, None, None)
t: BinarySearchTree[Int] = Tree(5,None,None)
scala> t = t.insert(3)
t: BinarySearchTree[Int] = Tree(5,Some(Tree(3,None,None)),None)
scala> t = t.insert(4)
t: BinarySearchTree[Int] = Tree(5,Some(Tree(3,None,Some(Tree(4,None,None)))),None)
scala> t = t.insert(7)
t: BinarySearchTree[Int] = Tree(5,Some(Tree(3,None,Some(Tree(4,None,None)))),Some(Tree(7,None,None)))
我同意@dhg的建议,即探索不可变的树数据结构作为当前方向的替代方案。然而,如果一棵可变的树是你真正需要的,那么请继续阅读 您眼前的问题是
this.type
在BinaryTree[T]
的定义中并不是您认为它的意思。它实际上是封闭实例的单例类型,即this
所居住的类型。这里有一个例子说明
scala> class Foo { def self : this.type = this /* OK */ }
defined class Foo
scala> class Bar { def self : this.type = new Bar /* Does not compile */ }
<console>:7: error: type mismatch;
found : Bar
required: Bar.this.type
class Bar { def self : this.type = new Bar /* Does not compile */ }
^
谢谢这两个答案都提供了大量的信息,但这一个更直接地抓住了我的意图。我一直在反复考虑是否接受你或另一个答案。迈尔斯通过讨论如何细化派生类中的子树类型,找到了问题的核心,但这个答案对于让我了解一些更好的Scala习惯用法非常有帮助。
scala> var t = new BinarySearchTree(5, None, None)
t: BinarySearchTree[Int] = Tree(5,None,None)
scala> t = t.insert(3)
t: BinarySearchTree[Int] = Tree(5,Some(Tree(3,None,None)),None)
scala> t = t.insert(4)
t: BinarySearchTree[Int] = Tree(5,Some(Tree(3,None,Some(Tree(4,None,None)))),None)
scala> t = t.insert(7)
t: BinarySearchTree[Int] = Tree(5,Some(Tree(3,None,Some(Tree(4,None,None)))),Some(Tree(7,None,None)))
scala> class Foo { def self : this.type = this /* OK */ }
defined class Foo
scala> class Bar { def self : this.type = new Bar /* Does not compile */ }
<console>:7: error: type mismatch;
found : Bar
required: Bar.this.type
class Bar { def self : this.type = new Bar /* Does not compile */ }
^
abstract class BinaryTree[T] {
type Self <: BinaryTree[T]
var contents : T
var l: Self = _
var r: Self = _
}
class BinarySearchTree[T <: Ordered[T]](stored_value : T) extends BinaryTree[T] {
type Self = BinarySearchTree[T]
var contents = stored_value
def insert(newval: T) {
if (newval <= contents) {
if (l == null) {
new BinarySearchTree(newval)
} else {
l.insert(newval)
}
} else {
if (r == null) {
new BinarySearchTree(newval)
} else {
r.insert(newval)
}
}
}
}