简体   繁体   English

为什么这个 goroutine 的行为就像是通过引用调用?

[英]Why does this goroutine behave like it's call by reference?

I'm trying to learn the basics of Go and I'm a bit confused about the difference between call by value and call by reference in a code snippet I tested.我正在尝试学习 Go 的基础知识,我对我测试的代码片段中按值调用和按引用调用之间的区别感到有些困惑。

I tried to solve a coding game puzzle in which a solution for a tic-tac-toe field is to be calculated.我试图解决一个编码游戏难题,其中要计算井字游戏领域的解决方案。


The code I'm using我正在使用的代码

Because I'm learning Go, I wanted to use a goroutine to test every field of the tic-tac-toe board, check whether this field is the solution and then put a pointer to this field in a channel for the main method to have the result.因为我正在学习Go,所以我想用一个goroutine来测试井字板的每个字段,检查这个字段是否是解决方案,然后将指向该字段的指针放在通道中,以便主要方法具有结果。 The code I used looks like this:我使用的代码如下所示:

package main

import "fmt"
import "os"

var player int = int('O')
var opponent int = int('X')
var empty int = int('.')

type board struct {
    fields [][]int
}

func main() {
    lines := [3]string {"OO.", "...", "..."}

    var b board
    b.fillBoard(lines)
    fmt.Fprintln(os.Stderr, "input board:")
    b.printBoard(true)

    resultChannel := make(chan *board)
    for i := 0; i < 3; i++ {
        for j := 0; j < 3; j++ {
            go tryField(b, [2]int{i, j}, resultChannel) // goroutine call that isn't working as expected
        }
    }

    fmt.Fprintln(os.Stderr, "\nresult:")
    for i := 0; i < 9; i++ {
        resultBoard := <- resultChannel
        if (resultBoard != nil) {
            resultBoard.printBoard(false)
            return
        }
    }
    
    // fmt.Fprintln(os.Stderr, "Debug messages...")
    fmt.Println("false")// Write answer to stdout
}

func tryField(b board, field [2]int, result chan *board) {
    b.printBoard(true)
    fmt.Fprintln(os.Stderr, "add O to field: ", field)
    fmt.Fprint(os.Stderr, "\n")
    if (b.fields[field[0]][field[1]] != empty) {
        result <- nil
    }

    b.fields[field[0]][field[1]] = player
    if (b.isWon()) {
        result <- &b
    } else {
        result <- nil
    }
}


func (b *board) fillBoard(lines [3]string) {
    b.fields = make([][]int, 3)
    for i := 0; i < 3; i++ {
        b.fields[i] = make([]int, 3)
    }

    for i, line := range lines {
        for j, char := range line {
            b.fields[i][j] = int(char)
        }
    }
}

func (b *board) printBoard(debug bool) {
    var stream *os.File
    if (debug) {
        stream = os.Stderr
    } else {
        stream = os.Stdout
    }
    for i := 0; i < 3; i++ {
        for j := 0; j < 3; j++ {
            fmt.Fprint(stream, string(b.fields[i][j]))
        }
        fmt.Fprint(stream, "\n")
    }
}

func (b *board) isWon() bool {
    for i := 0; i < 3; i++ {
        rowFull := true
        colFull := true
        for j := 0; j < 3; j++ {
            rowFull = rowFull && b.fields[i][j] == player
            colFull = rowFull && b.fields[j][i] == player
        }
        if (rowFull || colFull) {
            return true
        }
    }

    diagonal1Full := true
    diagonal2Full := true
    for i := 0; i < 3; i++ {
       diagonal1Full = diagonal1Full && b.fields[i][i] == player
       diagonal2Full = diagonal2Full && b.fields[i][2-i] == player
    }

    if (diagonal1Full ||diagonal2Full) {
        return true
    }

    return false
}

You can run it in the go playground .您可以在go 操场上运行它。

The problem问题

Since the last function in the snippet is declared as func tryField(b board, field [2]int, result chan *board) I assume the board b to be an indipendent copy, each time I call the method, because it's call by value.由于片段中的最后一个 function 被声明为func tryField(b board, field [2]int, result chan *board)我假设 board b是一个独立的副本,每次我调用该方法时,因为它是按值调用. So changing this board should not affect the other boards in the other goroutines.所以改变这个板不应该影响其他 goroutines 中的其他板。 But unfortunately changing the board in one goroutine does affect the boards in the other goroutines as the output of the programm is the following:但不幸的是,在一个 goroutine 中更改板确实会影响其他 goroutine 中的板,因为程序的 output 如下:

input board:输入板:
OO.哦。
... ...
... ...

result:结果:
OO.哦。
... ...
... ...
add O to field: [1 0]将 O 添加到字段:[1 0]

OO.哦。
O..哦..
... ...
add O to field: [2 1]将 O 添加到字段:[2 1]

OO.哦。
O..哦..
.O. .O.

As you can see the initial field has two O's at the first and the second col in the first line.如您所见,初始字段在第一行的第一列和第二列有两个 O。 Adding an O to the position [1 0] works like expected, but when adding an O to the field [2 1] the there is also an O at [1 0], which was added in the previous goroutine and shouldn't be there since it's call by value.向 position [1 0] 添加一个 O 的工作方式与预期的一样,但是当向字段 [2 1] 添加一个 O 时,在 [1 0] 处也有一个 O,这是在之前的 goroutine 中添加的,不应该添加因为它是按值调用的。


The question问题

Why does the code in my snippet behave like it's call by reference although the function doesn't use a pointer?尽管 function 不使用指针,为什么我的代码片段中的代码表现得像引用调用一样?

Thanks in advance !提前致谢 !

Slices are references to arrays.切片是对 arrays 的引用。 When modifying a slice without copying it, the underlaying array will be modified.当修改一个切片而不复制它时,底层数组将被修改。 Therefore, all slices that point to the same underlaying array will see this change.因此,指向同一个底层数组的所有切片都会看到这种变化。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM