Scala:在元素列表的元素之间进行递归搜索

时间:2012-11-26 09:03:06

标签: scala search recursion constraints

如果我有这样的课程:

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。

2 个答案:

答案 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才能编译上述测试代码。

至少这是我所看到的 - 但是可能有一些技巧可以解决这个问题,例如懒惰的评价,我还没有很熟悉。如果我错了,请有人纠正我。

更新2

  

类型不匹配;找到:a0Dominio.ArchitecturalElement必填:a0Dominio.SAComponent

我认为此处可能存在拼写错误:根据您的方法声明,SAComponent应该是Component不应该吗?

无论如何,问题是您将subElements声明为Set[ArchitecturalElement],因此其元素显然属于ArchitecturalElement类型,而不是Component(继承关系是另一种方式)。因此,要么将subElements更改为Set[Component],要么将函数参数ancestorcurrent声明为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())
}