当玩玩具示例 - 位置时,骑士可以在n
移动后从棋盘上到达,从某个位置开始x
- 我想知道是否有存在一个更清晰的解决方案(在简洁和函数式编程意义上)
flatMap
(暂时忽略filter
)一定次数(每次移动一次)move(...)
move2(...)
object ChessFun {
import scala.annotation.tailrec
case class Position(x: Int, y: Int)
case class Chessboard(widthX: Int, widthY: Int) {
def positionOnBoard(pos: Position) = {
(0 <= pos.x) && (pos.x < widthX) && (0 <= pos.y) && (pos.y < widthY)
}
}
def knightMoves(pos: Position) = Set(
Position(pos.x + 1, pos.y + 2),
Position(pos.x + 2, pos.y + 1),
Position(pos.x + 1, pos.y - 2),
Position(pos.x + 2, pos.y - 1),
Position(pos.x - 1, pos.y + 2),
Position(pos.x - 2, pos.y + 1),
Position(pos.x - 1, pos.y - 2),
Position(pos.x - 2, pos.y - 1)
)
def move(startPos: Position, steps: Int, chessboard: Chessboard) : Set[Position] = {
@tailrec
def moveRec(accum: Set[Position], remainingSteps: Int) : Set[Position] = {
remainingSteps match {
case 0 ⇒ accum
// otherwise
case _ ⇒ {
// take a position and calculate next possible positions
val a: Set[Position] = accum
.flatMap( pos ⇒ knightMoves(pos)
.filter( newPos ⇒ chessboard.positionOnBoard(newPos)) )
moveRec(a, remainingSteps - 1)
}
}
}
moveRec(Set(startPos), steps)
}
def move2(startPos: Position, steps: Int, chessboard: Chessboard) : Set[Position] = {
val nextFnc : Set[Position] => Set[Position] = {
positions => positions
.flatMap( pos ⇒ knightMoves(pos)
.filter( newPos ⇒ chessboard.positionOnBoard(newPos)) )
}
// nexts composes nextFnc `steps` times
val nexts = (0 until steps).map( i ⇒ nextFnc).reduce( _ compose _)
// apply nexts
nexts(Set(startPos))
}
def main(args: Array[String]): Unit = {
val startPos = Position(0,0)
println( move( Position(0,0), 2, Chessboard(8, 8)) )
println( move2( Position(0,0), 2, Chessboard(8, 8)) )
}
}
从Alvaro Carrasco给出的答案中得到一些启发,我已经优雅地重写了方法move2
:
def move2b(startPos: Position, steps: Int, chessboard: Chessboard) : Set[Position] = {
val nextFnc : Set[Position] => Set[Position] = {
_.flatMap( knightMoves(_).filter( chessboard.positionOnBoard(_)) )
}
List.fill(steps)(nextFnc).reduce(_ compose _)(Set(startPos))
}
Kleisli
? (不是指责,我想要论点;))答案 0 :(得分:3)
不知道是否还有FP,但这是一个使用scalaz >=>
的版本。
import scalaz.Kleisli
import scalaz.syntax.kleisli._
import scalaz.std.list._
def move3 (startPos: Position, steps: Int, chessboard: Chessboard) : Set[Position] = {
val validMove = Kleisli {a: Position => knightMoves(a).filter(chessboard.positionOnBoard).toList}
List.fill(steps)(validMove).reduce(_ >=> _)(startPos).toSet
}
必须使用List,因为Set没有Bind实例。
更新:已移除step-1
,这是我之前尝试的旧版本中的遗留物。