假设我正在使用回溯解决问题(例如N-Queen
)。如果我想找到唯一的(第一个)解决方案而不是全部解决方案,该怎么办?
我想我可以强制执行(例如使用可变的布尔标志)。我想知道我是如何做到的功能。
答案 0 :(得分:14)
虽然Scala的Option
可以在这里工作,正如其他两个答案所指出的那样,更惯用的功能方法是使用Scala中的“懒惰列表” - 或Stream
来表示一套解决方案。
我发现自己编写了这样的代码,例如:
trait Node[A] {
def children: Stream[A with Node[A]]
def dfs(f: A => Boolean): Stream[A] = this.children.flatMap {
child => if (f(child)) Stream(child) else child.dfs(f)
}
}
现在假设我有一个扩展Board
的{{1}}类,并且具有Node[Board]
方法的实现,该方法返回所有有效的板,另外还有一块。假设它还有一些其他有用的东西,比如children
方法,带size
的伴随对象等。
然后,我可以编写以下内容以获得empty
解决方案:
Stream
val solutions = Board.empty.dfs(_.size == 8)
是懒惰的,只评估它的头部,所以现在我们只搜索了足够远的树来找到第一个解决方案。我们可以使用Stream
:
head
或者其他什么。但如果我需要,我也可以得到其他结果:
scala> solutions.head
res1: Board =
o . . . . . . .
. . . . o . . .
. . . . . . . o
. . . . . o . .
. . o . . . . .
. . . . . . o .
. o . . . . . .
. . . o . . . .
这会搜索足够多的树以找到第十个解决方案,然后停止。
scala> solutions(10)
res2: Board =
. o . . . . . .
. . . . . . o .
. . . . o . . .
. . . . . . . o
o . . . . . . .
. . . o . . . .
. . . . . o . .
. . o . . . . .
优于Stream
方法的一大优势是,如果我需要它,我可以获得额外的结果,而不需要为第一个付出更多。
答案 1 :(得分:5)
这是一个简单的情况,深度优先搜索在找到它正在寻找的内容时停止。它使用了Option
,如Chris K的答案所述。
case class Tree[A](v: A, subtrees: Tree[A]*) {
def dfs(s: A): Option[A] = {
println("visiting " + v)
subtrees.foldLeft(if(v == s) Some(v) else None)((r, t) =>
if(r.isDefined)
r
else
t.dfs(s)
)
}
override def toString() = "Tree(%s%s%s)".format(v, if(subtrees.nonEmpty) ", " else "", subtrees.mkString(", "))
}
用法:
scala> val t = Tree(1, Tree(2, Tree(3), Tree(4)), Tree(5, Tree(6), Tree(7)))
t: Tree[Int] = Tree(1, Tree(2, Tree(3), Tree(4)), Tree(5, Tree(6), Tree(7)))
树t
看起来像
1
/ \
2 5
/ \ / \
3 4 6 7
因此,我们可以搜索元素并跟踪它访问的节点:
scala> t.dfs(6)
visiting 1
visiting 2
visiting 3
visiting 4
visiting 5
visiting 6
res42: Option[Int] = Some(6)
请注意,在找到我们正在寻找的内容后,我们不再访问任何节点。
答案 2 :(得分:2)
假设您正在使用递归搜索功能,您的函数应该返回结果(即皇后的定位)或者指示找不到该分支的结果。 Scala可能有一个选项/类型,你可以使用它。该建议同样适用于任何功能语言。