Go语言嵌套循环作用域问题:井字棋状态生成异常排查
井字棋下一步状态生成的Go语言问题排查
问题代码片段
var next []State for row := 0; row < len(board); row++ { for place := 0; place < len(board[row]); place++ { if board[row][place] == 0 { nPos := board fmt.Print(nPos) nPos[row][place] = play fmt.Print(nPos, row, place, play, "\n") next = append(next, nPos) } } }
其中State是[][]int的类型别名,board为State类型,play是int类型,next是[]State类型。
运行输出
[[0 0 0] [0 0 0] [0 0 0]][[1 0 0] [1 0 0] [1 0 0]] 0 0 1 [[1 0 0] [1 0 0] [1 0 0]][[1 1 0] [1 1 0] [1 1 0]] 0 1 1 [[1 1 0] [1 1 0] [1 1 0]][[1 1 1] [1 1 1] [1 1 1]] 0 2 1 [[[1 1 1] [1 1 1] [1 1 1]] [[1 1 1] [1 1 1] [1 1 1]] [[1 1 1] [1 1 1] [1 1 1]]]
异常现象
- 单次迭代修改会影响整列元素
- 修改结果会被保留,
nPos并未在每次迭代中得到全新的棋盘状态
问题根源与解决方案
核心问题:切片的引用特性
你错误地认为nPos := board会创建一个全新的棋盘,但Go中切片是引用类型,赋值操作只是复制了切片的头部信息(包含指向底层数组的指针、长度、容量),而非整个数组的副本。这意味着:
nPos和原board共享同一个底层数组,修改nPos的元素等同于直接修改board- 每次
append到next中的都是同一个引用,最终next里的所有元素都会指向最后修改完成的那个棋盘
整列修改的额外原因
如果你的board是通过以下错误方式初始化的,会导致所有行共享同一个切片:
// 错误初始化方式 row := make([]int, 3) board := State{row, row, row}
此时修改任意一行的某个元素,所有行对应位置的元素都会同步变化,因为它们指向同一个底层数组。正确的初始化应该为每行创建独立的切片:
// 正确初始化方式 board := make(State, 3) for i := range board { board[i] = make([]int, 3) }
修复代码
要生成独立的下一步棋盘状态,需要对原棋盘进行深拷贝,创建全新的二维切片:
// 定义深拷贝函数 func copyBoard(board State) State { newBoard := make(State, len(board)) for i := range board { newBoard[i] = make([]int, len(board[i])) copy(newBoard[i], board[i]) } return newBoard } // 修改后的主逻辑 var next []State for row := 0; row < len(board); row++ { for place := 0; place < len(board[row]); place++ { if board[row][place] == 0 { nPos := copyBoard(board) // 替换为深拷贝 nPos[row][place] = play next = append(next, nPos) } } }
这样每次迭代都会生成一个完全独立的棋盘副本,修改操作不会影响原棋盘和其他已生成的状态。
内容的提问来源于stack exchange,提问作者JPSO
相关产品推荐
相关产品推荐

