如果我有这样的课程:
class Person (var name:String, var surname:String, var sons: Set[Person])
我希望控制一个人不能在他的儿子和他儿子的儿子之间遏制自己。 我怎么能这样做?
我原本以为是递归搜索。但我必须小心不要创造周期。 我可以使用布尔值作为后卫,只需找到一个包含自身的项目就会停止搜索。
我该如何实现?你有想法吗? 非常感谢你。
更新 非常感谢您的帮助。很好的答案,但最重要的是你有很棒的想法。 现在我只需要一点帮助就可以在我的小项目上测试这个检查。
我的实际情况如下:
trait ArchitecturalElement extends PropertyHolderElement with TypedElement{}
abstract class Component extends ConnectableElement with ArchitecturalElement {
var subElements : Set[ArchitecturalElement]
var interactionPoints : Set[InteractionPoint]
//Here I put the control
//A component cannot contain himself in his subElements and in subElements of it subelement
def notHerOwnDescendant = {
def notDescendant(ancestor: Component, current: Component, checked: Set[ArchitecturalElement]): Boolean =
!current.subElements.contains(ancestor) && current.subElements.forall(
p => checked.contains(p) || notDescendant(ancestor, p, checked + p))
notDescendant(this, this, Set())
}
}//Component
abstract class InteractionPoint extends ConnectableElement{}
class SAInterface( var name : String,
var description : String = "empty",
var direction : SAInterfaceDirection = null
)extends InteractionPoint with ArchitecturalElement
class SAComponent ( var name :String,
var description : String = "empty",
var subElements : Set[ArchitecturalElement] = Set(),
var interactionPoints : Set[InteractionPoint] = Set()
) extends Component
但我有一个不兼容的类型:
类型不匹配;发现:a0Dominio.ArchitecturalElement必需:a0Dominio.SAComponent
p => checked.contains(p) || notDescendant(ancestor, p, checked + p)
// ^ here
从Set [ArchitecturalElement]中,我派生出一个Set [Component]? 而Component继承自ArchitecturalElement。
答案 0 :(得分:2)
以下方法生成当前人物的所有后代,而不会在周期中丢失:
def descendants(stop: Set[Person] = Set()): Set[Person] =
if(stop contains this) Set()
else this.sons.flatMap(descendants(_,stop + this)) + this
您可以用它来检查您的病情。为了检测任意周期,一旦你最终进入if
的第一个分支,你就会得到一个肯定的结果。
另一种方法是在构造函数/ setter中强制图形为非循环。这允许您在没有停止集的情况下使用搜索,因为所有现有实例都保证无循环,并且可以使用稍微更简单和更快速的方法来测试条件。
答案 1 :(得分:1)
如果我理解正确,你需要这样的东西:
class Person(...) {
...
def notHerOwnDescendant =
!sons.contains(this) && sons.forall(!_.sons.contains(this))
...
}
否则,如果你想要一路走下去:
class Person(...) {
...
def notDescendant(person: Person) =
!sons.contains(person) && sons.forall(_.notDescendant(person))
def notHerOwnDescendant =
notDescendant(this)
...
}
免责声明:我无法在IDE中测试此代码,因此无法保证其编译和正确。尽管如此,我希望它至少可以作为思想的食物: - )
这是一个经过更新和测试的版本,使用@gilad提到的“图形着色”解决方案来处理周期:
class Person (val name:String, val surname:String, var sons: Set[Person]) {
def notHerOwnDescendant = {
def notDescendant(ancestor: Person, current: Person, checked: Set[Person]): Boolean =
!current.sons.contains(ancestor)
&& current.sons.forall(
p => checked.contains(p) || notDescendant(ancestor, p, checked + p))
notDescendant(this, this, Set())
}
}
一些测试代码:
val abel = new Person("", "Abel", Set())
val cain = new Person("", "Cain", Set())
val adam = new Person("", "Adam", Set(abel, cain))
println("Adam is not his own descendant: " + adam.notHerOwnDescendant)
cain.sons += cain
println("Added Cain to his sons' set")
println("Adam is not his own descendant: " + adam.notHerOwnDescendant)
abel.sons += adam
println("Added Adam to his grandsons' set")
println("Adam is not his own descendant: " + adam.notHerOwnDescendant)
输出:
Adam is not his own descendant: true
Added Cain to his sons' set
Adam is not his own descendant: true
Added Adam to his grandsons' set
Adam is not his own descendant: false
作为旁注,我相信你可以通过坚持val
属性而不是var
来阻止一个人成为他自己的后代。如果sons
的集合是不可变的,则无法在图形中创建循环,因为在创建父项之前需要准备sons
,并且不能将父项添加到任何现有的后代儿子们。请注意,我必须将sons
保留为var
才能编译上述测试代码。
至少这是我所看到的 - 但是可能有一些技巧可以解决这个问题,例如懒惰的评价,我还没有很熟悉。如果我错了,请有人纠正我。
类型不匹配;找到:
a0Dominio.ArchitecturalElement
必填:a0Dominio.SAComponent
我认为此处可能存在拼写错误:根据您的方法声明,SAComponent
应该是Component
不应该吗?
无论如何,问题是您将subElements
声明为Set[ArchitecturalElement]
,因此其元素显然属于ArchitecturalElement
类型,而不是Component
(继承关系是另一种方式)。因此,要么将subElements
更改为Set[Component]
,要么将函数参数ancestor
和current
声明为ArchitecturalElement
。
Person
的新成员方法,用于查找后代图中属于周期的所有人:
def descendantsWithCycle = {
def findCycle(current: Person, checked: Set[Person]): Set[Person] =
if (checked contains current) Set(current)
else {
val newChecked = checked + current
current.sons.flatMap(p => findCycle(p, newChecked))
}
findCycle(this, Set())
}