Golang的新手 - 对指针感到困惑,请帮助转换c ++代码段

时间:2014-11-04 05:30:18

标签: go

我是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
}

1 个答案:

答案 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中有两个简单的解决方案:

  1. 使用指向数组的指针。与grid *[N][N]int中一样。这很好用,但访问和存储数组中的东西有点不干净。您必须使用(*grid)[i][j]显式取消引用看起来很难看的指针,并且可能有点难以阅读。

  2. 使用切片。这是更好的选择,更具有惯用的Go风格。它还避免了令人讨厌的全局常数到处都是。权衡是你牺牲了一些关于列大小的先验保证。

  3. 我会用一个带有几条评论的切片重写几个方法,剩下的就留给你了:

    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
    }
    

    杂项说明:

    1. 之前使用常量N的地方,您现在可以使用len(grid)。要获取列的长度,请使用len(grid[0])(警告:确保grid[0]存在)。

    2. 由于它是一个值,因此将[N][N]int作为参数或返回值几乎总是效率低下,因为它是每个函数调用的相当大的副本。指针追逐通常更快,除了非常小的N值(可能是1-2)。

    3. Go中不存在引用,除了极端技术意义,例如封闭变量的行为。

    4. 执行此操作的更好方法可能是使用某种At / Set方法设置声明类似type SudokuGrid struct { grid []int; rows,cols int }的内容,这样可以让您获得一些大小调整保证。我会离开这个(或者是否是一个好主意)由你决定。