我正在学习Scala(并且是函数式编程的新手)。 我已经创建了一个数独检查器(稍后它将是一个可以在数独中设置数字的游戏,也可能是一个自动数独求解器)。
检查行和列是否正确可以正常工作并以功能为导向。
虽然检查3x3正方形是正确的,但我写了一个非常难看的方法(见下面的代码)。
关于如何解决这个问题的任何提示可能更容易解决Scala'ish?
我找到了方法.slice(from,until),。splitAt(until),.sliding(size,step)并尝试再次使用'transpose'。虽然我不能提出一种有效的(更多)功能性方法来实现这一目标。
class Sudoku(){
val sudoku =
Array(
Array(0, 5, 0, 3, 0, 9, 0, 2, 6),
Array(3, 8, 9, 4, 2, 0, 1, 5, 7),
Array(4, 0, 6, 1, 0, 0, 0, 8, 9),
Array(0, 1, 3, 7, 9, 8, 0, 0, 4),
Array(0, 0, 8, 0, 0, 0, 5, 0, 0),
Array(0, 6, 0, 0, 0, 3, 0, 0, 0),
Array(0, 0, 1, 9, 3, 0, 0, 4, 0),
Array(9, 3, 5, 6, 4, 0, 8, 0, 1),
Array(0, 0, 2, 8, 7, 0, 0, 0, 5)
)
def checkSudoku(): Unit ={
println(check(sudoku))
}
private def check(sudoku: Array[Array[Int]]): Boolean = checkRows(sudoku) && checkCols(sudoku) && checkSquars(sudoku)
private def checkCols(sudoku: Array[Array[Int]]) = checkRows(sudoku.transpose)
private def checkRows(sudoku: Array[Array[Int]]): Boolean = sudoku.forall(row => checkRow(row))
private def checkRow(row: Array[Int]): Boolean = row.distinct.length == row.length
private def checkSquars(sudoku: Array[Array[Int]]): Boolean ={
val squared : Array[Array[Int]] = Array.ofDim[Int](1,3)
for(i <- 0 to (sudoku.length-1)/3) {
for(o <- 0 to (sudoku(i).length-1)/3) {
squared((i*3)+o) = sudoku(0+(i*3)).slice(0+(o*3), 3+(o*3)) ++ sudoku(1+(i*3)).slice(0+(o*3), 3+(o*3)) ++ sudoku(2+(i*3)).slice(0+(o*3), 3+(o*3))
}
}
squared.forall(row => checkRow(row))
}
}
val sudoku = new Sudoku()
sudoku.checkSudoku();
答案 0 :(得分:1)
这是一种(稍微有点脑筋急忙)的方法来执行checkSquares方法:
val rowBlocks = sudoku.grouped(3).toArray
def splitRow(row: Array[Int]) = row.grouped(3).toArray
val squares = rowBlocks.map( block => block.map(splitRow).transpose)
你应该说服自己squares
是数独的正方形的3x3阵列。现在您只需要检查条件:
squares.forall(_.forall(sq => sq.flatten.distinct.length == sq.flatten.length))
答案 1 :(得分:1)
这是将所有方块作为线条的替代方法。我试图让它更具可读性:
val squareSize = 3
val boardSize = sudoku.length
val squareLines = for {
rowStart <- List.range(0, boardSize, squareSize)
colStart <- List.range(0, boardSize, squareSize)
} yield {
List.range(0, squareSize).flatMap {
i =>
sudoku(rowStart + i).slice(colStart, colStart + squareSize)
}
}
然后,您可以使用squareLines
检查checkRow
,就像您在方法中所做的那样。
也可以使用rowStart
上的colStart
生成combinations(2)
和List.range
对,但我认为这种情况过于冗长。
答案 2 :(得分:0)
我开始使用list / array来管理拼图但很久以后,我发现使用地图更好。
接下来,使用Array [Set [Int]]而不是Array [Array [Int]]来使用Set来删除组中的重复项。
我添加了函数conflict(...)
来检查冲突。您可以将它用作参考。当我继续进行更改时(即我不再使用此函数,因为我有其他方法来实现相同的目的,但它达到了目的)并快速将这个答案放在一起,它们可能无法编译。如果你需要帮助,我希望你能得到这个想法并给我一个大喊。
val digits = ('1' to '9').mkString
val alphas = ('A' to 'I').mkString
def cross(rows: String, cols: String) = for {
row <- rows
col <- cols
} yield {"" + row + col}
val verticals = digits.map(d => cross(alphas, d.toString))
val horizontals = alphas.map(a => cross(a.toString, digits))
val blocks = for {
rowBlk <- alphas.grouped(3)
colBlk <- digits.grouped(3)
} yield (cross(rowBlk, colBlk))
val all = horizontals ++ verticals ++ blocks
def everyone = cross(alphas, digits).foldLeft(Map.empty[String, Set[Set[Int]]])((m,a) => m + (a -> (all.filter(_.contains(a)).map(_.toSet).toSet)))
def conflict(cell: String, solution: Map[String, Set[Int]]) =
if (everyone(cell)
.map(cells => cells.toList.foldLeft(List.empty[Int])((chars, c) =>
if (solution(c).size > 1) chars else chars ++ solution(c).toList))
.forall(xs => xs.size == xs.distinct.size)) solution else Map.empty