Scala:元素列表中元素之间的递归搜索

Scala:元素列表中元素之间的递归搜索,scala,search,recursion,constraints,Scala,Search,Recursion,Constraints,如果我有这样一门课: class Person (var name:String, var surname:String, var sons: Set[Person]) 我想要一种控制,一个人不能在他的儿子和他的儿子之间控制自己。 我该怎么做 我曾想过递归搜索。但我必须小心不要创造循环。 我可以使用一个布尔值作为保护,只要你找到一个包含它自己的项,它就会停止搜索 我如何实现这一点?你有什么想法吗? 多谢各位 更新 非常感谢你的帮助。答案不错,但最重要的是你有很棒的想法。 现在我只需要最后一点帮

如果我有这样一门课:

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
从集合[ArchitecturalElement]派生出集合[Component]?
组件继承自ArchitecturalElement。

如果我理解正确,您需要这样的东西:

class Person (var name:String, var surname:String, var sons: Set[Person])
或者,如果你想一直往下走:

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
properties而不是
var
s,就可以防止一个人成为自己的后代。如果
子对象集
不可变,则无法在图形中创建循环,因为在创建父对象之前需要准备好
子对象集
,并且无法将父对象添加到任何现有子对象集。请注意,我必须将
sons
作为
var
保存,以便编译上述测试代码

至少我是这么看的-但是可能有一些技巧可以克服这一点,例如使用我还不是很有经验的惰性评估。如果我错了,请有人纠正我

更新2 类型失配;找到:
a0Dominio.ArchitecturalElement
必需:
a0Dominio.SAComponent

我认为这里可能有一个输入错误:根据您方法的声明,
SAComponent
应该是
Component
不是吗

无论如何,问题是您将
子元素
声明为
集合[ArchitecturalElement]
,因此其元素显然属于
ArchitecturalElement
类型,而不是
组件
(继承关系则相反)。因此,要么将
子元素
更改为
集合[组件]
,要么将函数参数
祖先
当前
声明为
架构元素

更新3
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())
}

以下方法生成当前人员的所有后代,但不会在循环中丢失:

def descendants(stop: Set[Person] = Set()): Set[Person] = 
  if(stop contains this) Set() 
  else                   this.sons.flatMap(descendants(_,stop + this)) + this
你可以用它来检查你的状况。为了检测任意循环,一旦进入
if
的第一个分支,就会得到一个肯定的结果


另一种方法是在构造函数/设置器中强制使图形是非循环的。这允许您在不使用停止集的情况下使用搜索,因为所有现有实例都保证是无循环的,并且可以使用更简单、更快的方法来测试条件。

只需一点注意:您有field
son
,有点让人困惑的是,son是一组人。可能是
sons
childrens
?基本上,您定义了一个有向图,希望确保它不包含循环?这是一个算法问题,很容易解决。只需进行BFS搜索,并对allready访问的节点(人员)进行“着色”。如果你到达一个“有色”节点(人),你就有了一个循环。。。意思是人的是他/她自己的后代…@om-nom-nom,注意children已经是复数:-)@om-nom-nom+1,但是
children
在没有s的情况下足够复数。请少一点
var
s,多一点
val
s!非常感谢你。我需要时间来研究这个解决方案。如果图形包含OP想要测试的循环,我将联系shortlyStack overflow。@ziggystar您对解决方案有何建议?您好,非常感谢您的帮助。现在我想在我的实际项目中尝试您的解决方案,只是我发现了一个类型不匹配。我写了有问题的问题。域名没有错,我只是检查了一下。我正在研究一个图形化的UML。我无法更改域。我甚至不能将函数参数祖先和当前声明为ArchitecturalElement,因为ArchitecturalElement没有子元素。现在我看看是否能找到解决办法-(