Reputation: 8795
I am taking a tour of the Go language and am trying to do the following example (populating 2D arrays): https://tour.golang.org/moretypes/18 -- see the code below...
In the example we are supposed to populate a 2D array of uint8 with values (based on a function of our choice), then displaying these as a photo/pattern.
In my code, yy
should be a [][]uint8
array populated with single rows of []uint8
(xx
).
For the pattern/data I kept it simple - with each index of yy (y-axis), I increase the values placed in rows of xx - so the data should output a row of 0's, then a row of 1's, then a row of 2's, etc.
Although my code appears to work correctly based on the output of the first Println statement below (listed inside the loop as I attempt to populate the 2D array yy), the final values stored in the yy 2d array are all rows of 255! It's somehow using the value of the last x-axis row for everything.
I think somehow instead of making new value assignments I am somehow "pointing" to a single row of x-axis data that's being over-written, so at the end I get a matrix filled with the values that went in that final row.
package main
import (
"fmt"
"golang.org/x/tour/pic"
)
func Pic(dx, dy int) [][]uint8 {
//HERE I DYNAMICALLY CREATE THE ARRAYS BASED ON WHAT I WAS TAUGHT PREVIOUSLY
//I UNDERSTAND THAT TECHNICALLY THESE MAY BE SLICES NOT ARRAYS ...
//AND THUS MAYBE POINT SOMEWHERE AND DON'T ACTUALLY STORE ALL THE DATA (???)
yy := make([][]uint8, dy)
xx := make([]uint8, dx)
for y := range yy {
for x := range xx {
xx[x] = uint8((y)) // FOR SIMPLICITY I MADE PATTERN ROW x = y
}
yy[y] = xx
//THIS OUTPUT IS CORRECT; ROW OF 0's, ROW OF 1's, ROW OF 2's, ETC
fmt.Println(y, yy[y])
}
for y := range yy {
//THIS OUTPUT IS INCORRECT; ALL 255!
//IT POPULATED ALL ROWS W/ VALUES FROM THE LAST LOOP
fmt.Println(y, yy[y])
}
return yy
}
func main() {
pic.Show(Pic)
}
Note: Because I am debugging, use of "fmt.Println()" abrogates the display of the photo in my hands (but don't worry about that aspect). If you delete the fmt.Println() calls you can get the photo to display (albeit with the wrong/unexpected data).
Upvotes: 2
Views: 71
Reputation: 165546
The problem is yy[y] = xx
does not copy xx
, it assigns a reference to it.
for y := range yy {
for x := range xx {
xx[x] = uint8((y))
}
// This is not a copy.
yy[y] = xx
fmt.Println(y, yy[y])
}
The first iteration y = 0
. It writes 0 to all of xx
, then yy[y] = xx
assigns a reference to xx to yy[0]
, not a copy of xx
.
The next iteration of the loop y = 1
. It writes 1 to all of xx
, the same xx
as before, which also alters the reference to xx
in yy[0]
. It then assigns a reference to xx
as yy[1]
. Repeat 256 times.
xx
, yy[0]
and yy[1]
and yy[2]
and so on all refer to the same data.
When you print yy
at the end, you're really just printing xx
256 times. You can see this clearly by printing the pointer value, %p
.
fmt.Printf("%3s %p\n", "xx", xx);
for y := range yy {
fmt.Printf("%03v %p\n", y, yy[y])
}
xx 0xc420096000
000 0xc420096000
001 0xc420096000
002 0xc420096000
003 0xc420096000
004 0xc420096000
005 0xc420096000
The solution is to allocate xx
inside the loop, so a new xx
is allocated each loop iteration.
for y := range yy {
xx := make([]uint8, dx)
for x := range xx {
xx[x] = uint8((y))
}
// Still takes a reference to xx, but a different xx each time.
yy[y] = xx
fmt.Println(y, yy[y])
}
Or you can copy xx
, but this approach is safer: there's no possibility to accidentally reuse the same reference.
Upvotes: 3
Reputation: 1778
It's because you are assigning (by reference) xx
to all places in yy
(as if yy := [][]uint8{ xx, xx, xx, xx, ... }
.
Try allocating a new xx
for each place in yy
:
yy := make([][]uint8, dy)
for y := range yy {
xx := make([]uint8, dx)
Upvotes: 3