我是golang的新手,正在慢慢学习。要学习,我选择将这段算法转换为golang" http://www.geeksforgeeks.org/backtracking-set-7-suduku/"。
我已经尽了最大努力,但golang指针比c ++指针更容易混淆
我尝试了很多不同的方法来使它工作,但指针的东西仍然让我紧张。我只是不能让下面提到的两个功能正常工作。有时输出是 - 没有找到解决方案。有时,它只输出未改变的网格
有人可以修复SolveSudoku()和FindUnassignedLocation()函数(以及其他任何东西,如果有的话)并解释如何在GO中使用引用和指针。
我也尝试在GO网站上阅读一些文档,但这并不是很好,而且我没有足够的经验来理解那里给出的极小的东西。
如果有人建议在这段代码中有更多可以改进的东西,那么我也将不胜感激? ,错误?,任何事情。
package main
import "fmt"
const (
UNASSIGNED = 0
N = 9
)
func SolveSudoku(grid [N][N]int) bool {
var row, col int
if !FindUnassignedLocation(grid, row, col) {
return true
}
for num := 1; num <= 9; num++ {
if isSafe(grid, row, col, num) {
grid[row][col] = num
if SolveSudoku(grid) {
return true
}
grid[row][col] = UNASSIGNED
}
}
return false
}
func FindUnassignedLocation(grid [N][N]int, row int, col int) bool {
for row = 0; row < N; row++ {
for col = 0; col < N; col++ {
if grid[row][col] == UNASSIGNED {
return true
}
}
}
return false
}
func UsedInRow(grid [N][N]int, row int, num int) bool {
for col := 0; col < N; col++ {
if grid[row][col] == num {
return true
}
}
return false
}
func UsedInCol(grid [N][N]int, col int, num int) bool {
for row := 0; row < N; row++ {
if grid[row][col] == num {
return true
}
}
return false
}
func UsedInBox(grid [N][N]int, boxStartRow int, boxStartCol int, num int) bool {
for row := 0; row < 3; row++ {
for col := 0; col < 3; col++ {
if grid[row+boxStartRow][col+boxStartCol] == num {
return true
}
}
}
return false
}
func isSafe(grid [N][N]int, row int, col int, num int) bool {
return !UsedInRow(grid, row, num) && !UsedInCol(grid, col, num) && !UsedInBox(grid, row-row%3, col-col%3, num)
}
func printGrid(grid [N][N]int) {
for row := 0; row < N; row++ {
for col := 0; col < N; col++ {
fmt.Printf("%2d", grid[row][col])
}
fmt.Printf("\n")
}
}
func main() {
var grid = [N][N]int{
[N]int{3, 0, 6, 5, 0, 8, 4, 0, 0},
[N]int{5, 2, 0, 0, 0, 0, 0, 0, 0},
[N]int{0, 8, 7, 0, 0, 0, 0, 3, 1},
[N]int{0, 0, 3, 0, 1, 0, 0, 8, 0},
[N]int{9, 0, 0, 8, 6, 3, 0, 0, 5},
[N]int{0, 5, 0, 0, 9, 0, 6, 0, 0},
[N]int{1, 3, 0, 0, 0, 0, 2, 5, 0},
[N]int{0, 0, 0, 0, 0, 0, 0, 7, 4},
[N]int{0, 0, 5, 2, 0, 6, 3, 0, 0},
}
if SolveSudoku(grid) == true {
printGrid(grid)
} else {
fmt.Printf("No solution exists")
}
return
}
答案 0 :(得分:5)
你的问题并不是你不理解指针。指针在Go中的工作方式与在C ++中的工作方式大致相同(除了Go之外,还有像逃逸分析和GC这样的东西,你不必担心悬空或无效的指针,而且没有指针运算的可疑代价)。
您的问题是对C ++中的数组与Go之间差异的误解。在C ++中,数组实际上是一个指针,语法是别名。执行int a[9]
和int *a
的类型相同,静态大小调整和自由初始化只是一个很酷的编译器技巧。访问数组是指针算术的精确语法,然后是解除引用 - 如上所述,这是Go没有的。
在Go中,数组是值而不是指针。当你有一个带a [9]int
的函数时,你实际上是告诉编译器复制九个整数值,而不是一个指向内存中恰好有九个整数值的位置的指针。将func(a [2]int)
视为写func(a1, a2 int)
的好方法。
这也导致了另一个微妙的代码差异,在C ++中int a[9][9]
是指向九个指针的指针,每个指针指向9个整数。在Go中,它是一个文字的,连续的RMO存储的9x9整数块。
Go中有两个简单的解决方案:
使用指向数组的指针。与grid *[N][N]int
中一样。这很好用,但访问和存储数组中的东西有点不干净。您必须使用(*grid)[i][j]
显式取消引用看起来很难看的指针,并且可能有点难以阅读。
使用切片。这是更好的选择,更具有惯用的Go风格。它还避免了令人讨厌的全局常数到处都是。权衡是你牺牲了一些关于列大小的先验保证。
我会用一个带有几条评论的切片重写几个方法,剩下的就留给你了:
func main() {
// We can omit the []int on every line, Go infers it
var grid = [][]int{
{3, 0, 6, 5, 0, 8, 4, 0, 0},
{5, 2, 0, 0, 0, 0, 0, 0, 0},
{0, 8, 7, 0, 0, 0, 0, 3, 1},
{0, 0, 3, 0, 1, 0, 0, 8, 0},
{9, 0, 0, 8, 6, 3, 0, 0, 5},
{0, 5, 0, 0, 9, 0, 6, 0, 0},
{1, 3, 0, 0, 0, 0, 2, 5, 0},
{0, 0, 0, 0, 0, 0, 0, 7, 4},
{0, 0, 5, 2, 0, 6, 3, 0, 0},
}
// == true is superfluous
if SolveSudoku(grid) {
printGrid(grid)
} else {
fmt.Println("No solution exists")
}
// Don't need to explicitly return, Go's mains are "void"
}
func UsedInCol(grid [][]int, col int, num int) bool {
// We can use range to iterate over the whole slice.
// The first value (which we ignore) is the slice index, the value you
// used to call "row".
// Now row is the slice containing the given row, this is similar to an iterator.
for _,row := range grid {
if row[col] == num {
return true
}
}
return false
}
杂项说明:
之前使用常量N
的地方,您现在可以使用len(grid)
。要获取列的长度,请使用len(grid[0])
(警告:确保grid[0]
存在)。
由于它是一个值,因此将[N][N]int
作为参数或返回值几乎总是效率低下,因为它是每个函数调用的相当大的副本。指针追逐通常更快,除了非常小的N值(可能是1-2)。
Go中不存在引用,除了极端技术意义,例如封闭变量的行为。
执行此操作的更好方法可能是使用某种At / Set方法设置声明类似type SudokuGrid struct { grid []int; rows,cols int }
的内容,这样可以让您获得一些大小调整保证。我会离开这个(或者是否是一个好主意)由你决定。