我想知道如何用函数式编程在Scala中实现广度优先搜索。以下是我的第一份不纯净的代码:
def bfs[S](init: S, f: S => Seq[S], finalS: S => Boolean): Option[S] = {
val queue = collection.mutable.Queue[S]()
queue += init
var found: Option[S] = None
while (!queue.isEmpty && found.isEmpty) {
val next = queue.dequeue()
if (finalS(next)) {
found = Some(next)
} else {
f(next).foreach { s => queue += s }
}
}
found
}
尽管我仅使用本地可变性(一个“var”和一个可变的“Queue”),但它并不是纯函数式的。
我想出了另一个版本:
case class State[S](q: Queue[S], cur: S)
def update[S](f: S => Seq[S])(s: State[S]) : State[S] = {
val (i, q2) = s.q.dequeue
val q3 = f(i).foldLeft(q2) { case (acc, i) => acc.enqueue(i)}
State(q3, i)
}
def bfs2[S](init: S, f: S => Seq[S], finalS: S => Boolean): Option[S] = {
val s = loop(State[S](Queue[S]().enqueue(init), init), update(f) _, (s: State[S]) => s.q.isEmpty || finalS(s.cur))
Some(s.cur)
}
def loop[A](a: A, f: A => A, cond: A => Boolean) : A =
if (cond(a)) a else loop(f(a), f, cond)
有没有更好的解决方案?是否可以使用cats/scalaz来减少一些样板代码?
Queue
,而是使用(不可变的)List
。并且去掉State
- 因为cur
总是位于队列的顶部 - 只需在向下遍历树时将工作List
传递即可。 - DimaQueue
,它更高效一些,但也是基于列表的。或者使用IndexedSeq
之类的东西来实现对最后一个元素的常数时间访问。 - Dima