我正在努力更好地理解函数式编程,并决定实现一些经典的图算法。我已经通过将BFS循环包装到尾递归函数来实现连接,但是这个代码看起来并不比命令式好。有没有办法更好地实现它(使用for
理解,monad,等等)?
object Graph {
def apply(n: Int) = new Graph(n, Vector.fill(n)(List.empty[Int]))
}
class Graph private (val n: Int, val adj: IndexedSeq[List[Int]]) {
def addEdge(u: Int, v: Int) = {
new Graph(n, adj.updated(u, v :: adj(u)).updated(v, u :: adj(v)))
}
def connected = {
@tailrec
def connectedIter(q: Queue[Int], visited: Seq[Boolean]) : Boolean = {
if(q.isEmpty) visited.forall(x => x) else {
val (v, newq) = q.dequeue
val newVisited = visited.updated(v, true)
connectedIter(adj(v).foldLeft(newq){(acc, x) => if(visited(x)) acc else acc.enqueue(x)}, newVisited)
}
}
connectedIter(Queue[Int](0), IndexedSeq.fill(n)(false))
}
}
P.S。使用固有的递归DFS看起来好一点
def reachableDfs(v: Int): Seq[Boolean] = reachableDfs(v, Vector.fill(n)(false))
private def reachableDfs(v: Int, visited: Seq[Boolean]) : Seq[Boolean] = {
val newV = visited.updated(v, true)
adj(v).filterNot{x => newV(x)}.foldLeft(newV){(acc, x) => reachableDfs(x, acc)}
}
答案 0 :(得分:1)
您可以使用Set
操作来表达这一点,尽管这可能效率较低:
object Graph {
def apply(n: Int) = new Graph(n, Vector.fill(n)(Set.empty))
}
class Graph private (val n: Int, val adj: IndexedSeq[Set[Int]]) {
def addEdge(u: Int, v: Int) = {
new Graph(n, adj.updated(u, adj(u) + v).updated(v, adj(v) + u))
}
def connected = {
@tailrec
def connectedIter(q: Queue[Int], visited: Set[Int]): Boolean = {
if (q.isEmpty) visited.size == n else {
val (v, newq) = q.dequeue
connectedIter(newq enqueue (adj(v) -- visited), visited ++ adj(v))
}
}
connectedIter(Queue(0), Set.empty)
}
}
答案 1 :(得分:0)
您总是可以尝试使用Graph数据类型的替代和更多功能方法:
Martin Erwig。 2001.归纳图和功能图算法。 J. Funct。程序。 11,5(2001年9月),467-492。 DOI = 10.1017 / S0956796801004075