我正在扫描此代码,并且我想了解它是如何工作的。
有两种可能的树:用于空集的树,以及由整数和两个子树组成的树。不变式:对于每个节点,右侧的节点包含的整数值均高于父节点,而左侧节点的整数值则低于父节点。
这是代码:
abstract class IntSet{
def incl(x: Int): IntSet // include element x in the IntSet
def contains(x: Int): Boolean // is x an element of the set?
def union(other: IntSet): IntSet // union of this and other set
}
object Empty extends IntSet{
def contains(x: Int): Boolean = false
def incl(x:Int): IntSet = new NonEmpty(x, Empty, Empty)
override def toString = "."
def union(other:IntSet): IntSet = other
}
class NonEmpty(elem: Int, left: IntSet, right: IntSet) extends IntSet{
def contains(x: Int): Boolean =
if (x < elem) left contains x
else if (x > elem) right contains x
else true
def incl(x: Int): IntSet =
if (x < elem) new NonEmpty(elem, left incl x, right)
else if (x > elem) new NonEmpty(elem, left, right incl x)
else this
override def toString = "{" + left + elem + right + "}"
def union(other:IntSet): IntSet =
((left union right) union other) incl elem
}
如何使用此数据结构?它如何实现持久性?它是如何工作的?
最佳答案
该代码直接映射到您提供的描述。
让我们以一个简单的示例来演示用法:首先有一个空集,说e
然后添加一个元素以获得另一个集合,例如s1
。然后,您将拥有
2套e
和s1
:
val e = Empty
e contains 42 // will be false
// create s1 from e
val s1 = e incl 1 // e does not change; it remains a reference to the Empty set.
// Now we have s1, a set that should contain (1) and nothing else.
s1 contains 1 // will be true
s1 contains 42 // will be false
我猜您已经熟悉了Scala速记,可以让您输入
s1 incl 1
代替s1.incl(1)
请注意,只能有一个空集,因此也是如此:
val s1 = Empty incl 1
然后,假设您要添加,例如
2
,以获得另一组s2
,其元素必须同时包含
{1, 2}
。val s2 = s1 incl 2
s2 contains 1 // true
s2 contains 2 // true
s2 contains 3 // false
因此,任何集合上的
incl
方法都需要一个元素并返回一个新集合-它不会更改集合(调用
include
方法的原始对象ob)。我们有两种类型的树集:空和非空,每个都有
incl
的实现:// Empty
def incl(x:Int): IntSet = new NonEmpty(x, Empty, Empty)
读取:“将元素添加到空(树)集会产生另一个集,这是一个非空树,只有根节点具有
1
值,并且左子树和右子树为空”。非空集具有一个构造函数参数
elem
,它表示树的根,并且对NonEmpty
中的所有方法可见。// Non-Empty
def incl(x: Int): IntSet =
if (x < elem) new NonEmpty(elem, left incl x, right)
else if (x > elem) new NonEmpty(elem, left, right incl x)
else this
读取:(按与上述if-else相反的顺序):
x
添加到其根元素也是x
的非空集给你相同的设置(
this
)x
的根元素的非空集添加元素x
给您另一个集,其中:x
已添加到其中”:right incl x
x
的根元素的非空集中添加元素x
给您另一个集,其中:x
已添加到其中”:left incl x
“持久性”是通过以下事实来实现的:没有任何树或子树被更改。在这个例子中
val s1 = Empty incl 1 // s1 is a tree with only a root(1) an no branches.
val s2 = s1 incl 2 // s2 is another tree with -
// - the same root(1),
// - the same left-subtree as s1, (happens to be Empty)
// - a new subtree which in turn is a tree with -
// - the root element (2)
// - no left or right brances.
s1 contains 1 // true
s1 contains 2 // false
s2 contains 1 // true
s2 contains 2 // true
val s3 = s2 incl -3 // s2.incl(-3)
// from s2 we get s3, which does not change s2's structure
// in any way.
// s3 is the new set returned by incl, whose
// - root element remains (1)
// - left subtree is a new tree that contains
// just (-3) and has empty left, right subtrees
// - right subtree is the same as s2's right subtree!
s3.contains(-3) // true; -3 is contained by s3's left subtree
s3.contains(1) // true; 1 is s3's root.
s3.contains(2) // true; 2 is contained by s3's right subtree
s3.contains(5) // false
我们仅使用
incl
从其他集合派生集合(树),而无需更改原始集合。这是因为在非常阶段,我们要么-现有结构,或,
contains
的工作方式相同:Empty
的实现可为任何输入返回false
。如果给定元素与其根元素相同,或者它的左或右子树包含它,NonEmpty
就会迅速返回true!