我有以下功能可以解决迷宫问题:
def getWayDepthFirst(maze: Maze, position: Position, way: List[Position]): List[List[Position]] = {
if (!canWalkOnCell(maze, position)) {
Nil
} else {
if (isExit(position, maze)) {
List(position :: way)
} else {
val explorationArea: List[Position] = List(position.north, position.east, position.south, position.west) filter (x => !way.contains(x) && canWalkOnCell(maze, x))
explorationArea.flatMap {
(
notYetVisitedPosition =>
getWayDepthFirst(maze, notYetVisitedPosition, position :: way))
}
}
}
}
据我所知,我将不得不在另一个线程上启动递归调用,因为每个递归步骤都将沿着迷宫的路径前进。
我怎样才能在Scala中执行此操作?有没有提供此课程的课程?
答案 0 :(得分:-3)
这是解决方案
import scala.concurrent._ ; import ExecutionContext.Implicits.global; import scala.concurrent.duration._
import scala.async.Async.{async, await}
def depthFirst(pos: Pos = Pos(0,0), way: List[Pos] = Nil): Future[List[List[Pos]]] = async {
if (!available(pos)) {
Nil
} else {
if (isExit(pos)) {
List(pos :: way)
} else {
val explorationArea: List[Pos] = Directions map (pos) filter (x => !way.contains(x) && available(x))
val listOfFutures = explorationArea. map {
notYetVisitedPosition => trace(depthFirst _, notYetVisitedPosition, pos :: way)
}
await{Future.sequence(listOfFutures) map {_.flatten}}
}
}
}
val solutions: Seq[List[Pos]] = Await.result(depthFirst(), Duration.Inf);
以下是我测试它的方式
object maze extends App {
def available(pos: Pos) = pos match {case Pos(x,y) => x >= 0 && y >= 0 && x < 4 && y < 4 }
def isExit(pos: Pos) = {Thread.sleep(20) ; pos == Pos(1,1)}
case class Pos(val x: Int, val y: Int) {
def inc(axis: Boolean, delta: Int) = axis match {
case true => Pos(x + delta, y)
case false => Pos(x, y + delta)
}
}
object Directions {
val ops = for {
axis <- List(true, false)
delta <- List(1, -1)
} yield (axis, delta)
def map(pos: Pos) = ops.map {case (axis, delta) => pos.inc(axis, delta)}
}
val time1 = System.currentTimeMillis()
def trace[T](body: (Pos, List[Pos]) => T, pos: Pos = Pos(0,0), way: List[Pos] = Nil): T = {
val path = (pos :: way).reverse.map {case Pos(x,y) => x + ":" + y} mkString ","
//println (path)
def time = System.currentTimeMillis() - time1
val commands = //"th " + Thread.currentThread().getName + ": " +
//path + " <= " +
(way.foldLeft (("", pos)){case ((path, cur), prev) => //println(cur + " - " + prev)
val command = (cur.x - prev.x, cur.y - prev.y) match {
case (-1, _) => "l"
case (1, _) => "r"
case (_, -1) => "d"
case (_, 1) => "u"
}
(path + command, prev)
}._1.reverse)
//println(commands + " in at " + time)
val result = body(pos, way)
//println(commands + " out at " + time)
result
}
def serial: List[List[Pos]] = {
def depthFirst(pos: Pos = Pos(0,0), way: List[Pos] = Nil): List[List[Pos]] = {
if (!available(pos)) {
Nil
} else {
if (isExit(pos)) {
List(pos :: way)
} else {
val explorationArea: List[Pos] = Directions map (pos) filter (x => !way.contains(x) && available(x))
explorationArea.flatMap {
(
notYetVisitedPosition =>
trace(depthFirst _, notYetVisitedPosition, pos :: way))
}
}
}
}
depthFirst()
}
def futures: List[List[Pos]] = {
import scala.concurrent._ ; import ExecutionContext.Implicits.global; import scala.concurrent.duration._
def depthFirst(pos: Pos = Pos(0,0), way: List[Pos] = Nil): Future[List[List[Pos]]] = {
if (!available(pos)) {
Future{Nil}
} else {
if (isExit(pos)) {
Future {List(pos :: way)}
} else {
val explorationArea: List[Pos] = Directions map (pos) filter (x => !way.contains(x) && available(x))
val listOfFutures = explorationArea. map {
notYetVisitedPosition => trace(depthFirst _, notYetVisitedPosition, pos :: way)
}
//listOfFutures.foldLeft(Future{List[List[Pos]]()}){case (acc, fllp) => acc flatMap {acc => fllp map {llp => llp ++ acc}}}
Future.sequence(listOfFutures) map {_.flatten} // this collection has the same effect as above
}
}
}
Await.result(depthFirst(), Duration.Inf);
}
def asyncAwait: Seq[List[Pos]] = {
import scala.concurrent._ ; import ExecutionContext.Implicits.global; import scala.concurrent.duration._
import scala.async.Async.{async, await}
def depthFirst(pos: Pos = Pos(0,0), way: List[Pos] = Nil): Future[List[List[Pos]]] = async {
if (!available(pos)) {
Nil
} else {
if (isExit(pos)) {
List(pos :: way)
} else {
val explorationArea: List[Pos] = Directions map (pos) filter (x => !way.contains(x) && available(x))
val listOfFutures = explorationArea. map {
notYetVisitedPosition => trace(depthFirst _, notYetVisitedPosition, pos :: way)
}
await{Future.sequence(listOfFutures) map {_.flatten}}
}
}
}
Await.result(depthFirst(), Duration.Inf);
}
def streams: Seq[List[Pos]] = {
import scala.concurrent._ ; import ExecutionContext.Implicits.global; import scala.concurrent.duration._
def depthFirst(pos: Pos = Pos(0,0), way: List[Pos] = Nil): Stream[List[Pos]] = {
if (!available(pos)) {
Stream{Nil}
} else {
if (isExit(pos)) {
Stream(pos :: way)
} else {
val explorationArea = Directions map (pos) filter (x => !way.contains(x) && available(x))
explorationArea.toStream flatMap {
notYetVisitedPosition => depthFirst(notYetVisitedPosition, pos :: way)
}
}
}
}
val future = Future.traverse(depthFirst())(x => Future{x})
Await.result(future, Duration.Inf);
}
List(
"serial" -> serial _,
//"streams" -> streams _,
"fut" -> futures _,
"await" -> asyncAwait _,
"stub" -> {() => List()} // stub handles comma when you comment the items above
) foreach { case (title, func) =>
val time1 = System.currentTimeMillis()
val result = func()
val time = title + " produced " + result.length + " paths in " + (System.currentTimeMillis() - time1) + " ms"
println(time)
//result map {_.reverse} foreach println ; println(time)
}
}
带输出
serial produced 114 paths in 10382 ms
fut produced 114 paths in 10432 ms
await produced 114 paths in 1381 ms <- 7.5 times faster
如果我们删除isExit中的人为延迟,您会看到期货版本不比串行执行快(并且速度慢4倍)。你不应该听取上面的专家的意见,因为他们说保护期货
futures will be executed one-after-another, likewise there is no parallelism
解释为unlimited parallelism.
granularity is too fine in this assignment, so that you do not get any benefit
。这是完全专业化的另一个标志。显然,持续很长时间并以指数方式爆炸的任务,就像这个任务一样,当然要在平行线上进行探索,特别是在根本上。问他们大型勘探区域有多大可能,例如4个独立的大问题分支has too fine granularity
?我想说的是,这是一个非常好的问题,因为它是一个适度的问题,也表明蛮力use of Futures
不起作用。您不能将函数调用包装到future中并获得并行执行。我的代码的问题不是微基准测试或问题的粒度。转换Future [解决方案列表]很困难,当你的depthFirst = Future {... subsolutions = future of lists}时,这是必要的。您可以将期货列表展平为Future [solutionsList],但您需要从未来提取solutionsList。回顾Eric Mayer的FRP讲座,我决定需要一些等待并转向异步等待解决方案。我的期货解决方案实际上是在主线程中执行的,因为为了解决问题,我在一个线程中做了所有事情并将结果包装到了未来,这是愚蠢的事情,但是专家们没有认识到它并且决定责怪缩影测试。当人们说白色是彻头彻尾的黑色时,不要相信。