iterating over over a 2D slice in go - go

I am taking the "Tour of Go", and had a question regarding the Exercise: Slices example. Currently I can create the picture by iterating over each index using the the [] operator, just like you could in C.
func Pic(dx, dy int) [][]uint8 {
pic := make([][]uint8, dy)
for i := range pic {
pic[i] = make([]uint8, dx)
for j := range pic[i] {
pic[i][j] = uint8(1)
}
}
return pic
}
However, when I try to do something like below, I get an panic: runtime error: index out of range error. I tried adding print statements and calling Pic(3, 3), which printed out a 3x3 array just fine.
func Pic(dx, dy int) [][]uint8 {
pic := make([][]uint8, dy)
for _, y := range pic {
y = make([]uint8, dx)
for _, x := range y {
x = uint8(1)
_ = x // x has to be used
//fmt.Print("1")
}
//fmt.Print("\n")
}
return pic
}
Any thoughts on what I am doing wrong?

The main problem is your attempt to do assignment. Check my example using your code; https://play.golang.org/p/lwoe79jQ70
What you actually get out of the latter implementation is a 3x0 array, all of the inner arrays are empty. The reason for this is because you're using the range variable for assignment which doesn't work. If the current index is 0, y != pic[0], pic[0] is assigned to y however, y is temporary storage, it typically is the same address and is over written on each iteration. So after the latter example executes, all your x direction arrays are empty, indexing into one causes a panic.
Basically you should just be using your first implementation because it works fine and is the way you would typically do this. But the take away is, when you do a, b := range Something b != Something[a], it is it's on instance, it goes out of scope at the bottom of the loop and assigning to it will not cause a state change to the collection Something, instead you must assign to Something[a] if you want to modify Something[a].

range copies the values from the slice you're iterating over.
See: http://golang.org/ref/spec#RangeClause
To clarify what happens see this simple code example and its output:
package main
import "fmt"
func main() {
s := "hi"
//s[0] = 'H' // cannot assign to s[0]
for _, v := range s {
fmt.Printf("%T, %[1]v, %X\n", v, &v)
v = 'H' // has no effect: this is local var not ref
}
fmt.Println(s)
}
The output is:
int32, 104, C0820042D4
int32, 105, C0820042D4
hi
As you see the address of variable v is not changing (C0820042D4) and v is local variable and range copies value to it, so changing v has no effect.
Here v is rune (int32 alias), A rune is an integer value identifying a Unicode code point, and you cannot assign to s[0] and this won’t compile: s[0] = 'H'
so v = 'H' has no effect on s, it is just local variable.

Related

Adding/subtracting two numeric strings

I have two variables with big numbers set as strings:
var numA = "340282366920938463463374607431768211456"
var numB = "17014118346046923173168730371588410572"
I want to be able to add and subtract these kinds of large string numbers in Go.
I know I need to use math/big but I still can not for the life of me figure out how, so any example help will be greatly appreciated!
You may use big.NewInt() to create a new big.Int value initialized with an int64 value. It returns you a pointer (*big.Int). Alternatively you could simply use the builtin new() function to allocate a big.Int value which will be 0 like this: new(big.Int), or since big.Int is a struct type, a simple composite literal would also do: &big.Int{}.
Once you have a value, you may use Int.SetString() to parse and set a number given as string. You can pass the base of the string number, and it also returns you a bool value indicating if parsing succeeded.
Then you may use Int.Add() and Int.Sub() to calculate the sum and difference of 2 big.Int numbers. Note that Add() and Sub() write the result into the receiver whose method you call, so if you need the numbers (operands) unchanged, use another big.Int value to calculate and store the result.
See this example:
numA := "340282366920938463463374607431768211456"
numB := "17014118346046923173168730371588410572"
ba, bb := big.NewInt(0), big.NewInt(0)
if _, ok := ba.SetString(numA, 10); !ok {
panic("invalid numA")
}
if _, ok := bb.SetString(numB, 10); !ok {
panic("invalid numB")
}
sum := big.NewInt(0).Add(ba, bb)
fmt.Println("a + b =", sum)
diff := big.NewInt(0).Sub(ba, bb)
fmt.Println("a - b =", diff)
Output (try it on the Go Playground):
a + b = 357296485266985386636543337803356622028
a - b = 323268248574891540290205877060179800884

Duplicating a list to a list with pointers

I have simplified the problem incredibly to a small runnable example of my issue, shown below:
package main
import "fmt"
type A struct {
Name string
}
func main() {
main_list := []A{A{"1"}, A{"b"}, A{"3"}}
second_list := make([]*A, 0)
fmt.Println("FIRST LIST:")
for _, x := range main_list {
fmt.Println(x.Name)
second_list = append(second_list, &x)
}
fmt.Println("SECOND LIST:")
for _, x := range second_list {
fmt.Println((*x).Name)
}
}
Which provides:
FIRST LIST:
1
b
3
SECOND LIST:
3
3
3
The simple task is creating main_list with some dummy structs. The real issue is creating references (pointers) from the values in main_list into second_list. I absolutely do not want copies of the structs, only pointers/references to the main_list structs. The second for loop iterates through the newly populated second_list and shows only the last value from main_list (3 in this example) three times.
I expect the issue arrises with the way I am using &x in the first loop. With the way I get all three values of second_list to all be the same struct instance; I am going to assume that I actually made a pointer to the for-loop's iterator (as if it were a reference?). So all the pointers in second_list will always be the last item referenced in the first loop.
The question: How could I make a pointer from what x was pointing to at that moment in the for loop?
You're adding the address of the same x in every call to append.
You could initialize a new x and copy the value:
for _, x := range main_list {
x := x
second_list = append(second_list, &x)
}
Or create a new x directly indexing the slice:
for i := range main_list {
x := main_list[i]
second_list = append(second_list, &x)
}
Or if you want the address of the original value in the slice, you can use:
for i := range main_list {
second_list = append(second_list, &main_list[i])
}

unexpected slice append behaviour

I encountered weird behaviour in go code today: when I append elements to slice in loop and then try to create new slices based on the result of the loop, last append overrides slices from previous appends.
In this particular example it means that sliceFromLoop j,g and h slice's last element are not 100,101 and 102 respectively, but...always 102!
Second example - sliceFromLiteral behaves as expected.
package main
import "fmt"
func create(iterations int) []int {
a := make([]int, 0)
for i := 0; i < iterations; i++ {
a = append(a, i)
}
return a
}
func main() {
sliceFromLoop()
sliceFromLiteral()
}
func sliceFromLoop() {
fmt.Printf("** NOT working as expected: **\n\n")
i := create(11)
fmt.Println("initial slice: ", i)
j := append(i, 100)
g := append(i, 101)
h := append(i, 102)
fmt.Printf("i: %v\nj: %v\ng: %v\nh:%v\n", i, j, g, h)
}
func sliceFromLiteral() {
fmt.Printf("\n\n** working as expected: **\n")
i := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
fmt.Println("initial slice: ", i)
j := append(i, 100)
g := append(i, 101)
h := append(i, 102)
fmt.Printf("i: %v\nj: %v\ng: %v\nh:%v\n", i, j, g, h)
}
link to play.golang:
https://play.golang.org/p/INADVS3Ats
After some reading, digging and experimenting I found that this problem is originated in slices referencing the same underlaying array values and can be solved by copying slice to new one before appending anything, however it looks quite... hesitantly.
What's the idomatic way for creating many new slices based on old ones and not worrying about changing values of old slices?
Don't assign append to anything other than itself.
As you mention in the question, the confusion is due to the fact that append both changes the underlying array and returns a new slice (since the length might be changed). You'd imagine that it copies that backing array, but it doesn't, it just allocates a new slice object that points at it. Since i never changes, all those appends keep changing the value of backingArray[12] to a different number.
Contrast this to appending to an array, which allocates a new literal array every time.
So yes, you need to copy the slice before you can work on it.
func makeFromSlice(sl []int) []int {
result := make([]int, len(sl))
copy(result, sl)
return result
}
func main() {
i := make([]int, 0)
for ii:=0; ii<11; ii++ {
i = append(i, ii)
}
j := append(makeFromSlice(i), 100) // works fine
}
The slice literal behavior is explained because a new array is allocated if the append would exceed the cap of the backing array. This has nothing to do with slice literals and everything to do with the internals of how exceeding the cap works.
a := []int{1,2,3,4,5,6,7}
fmt.Printf("len(a) %d, cap(a) %d\n", a, len(a), cap(a))
// len(a) 7, cap(a) 7
b := make([]int, 0)
for i:=1; i<8, i++ {
b = append(b, i)
} // b := []int{1,2,3,4,5,6,7}
// len(b) 7, cap(b) 8
b = append(b, 1) // any number, just so it hits cap
i := append(b, 100)
j := append(b, 101)
k := append(b, 102) // these work as expected now
If you need a copy of a slice, there's no other way to do it other than, copying the slice. You should almost never assign the result of append to a variable other than the first argument of append. It leads to hard to find bugs, and will behave differently depending on whether the slice has the required capacity or not.
This isn't a commonly needed pattern, but as with all things of this nature if you need to repeate a few lines of code multiple times, then you can use a small helper function:
func copyAndAppend(i []int, vals ...int) []int {
j := make([]int, len(i), len(i)+len(vals))
copy(j, i)
return append(j, vals...)
}
https://play.golang.org/p/J99_xEbaWo
There is also a little bit simpler way to implement copyAndAppend function:
func copyAndAppend(source []string, items ...string) []string {
l := len(source)
return append(source[:l:l], items...)
}
Here we just make sure that source has no available capacity and so copying is forced.

Misunderstanding the usage of := in Go

I was reading this doc and saw the following fragment:
The := syntax is shorthand for declaring and initializing a variable, e.g. for var f string = "short" in this case.
f := "short"
fmt.Println(f)
The point is: is it only for strings? Or is it dymanic enough to understand what datatype should it store?
And plus: isn't it the same of var f = "short"?
Of course it infers the obvious type(s) returned by the expression on the right side.
The specification gives those examples :
i, j := 0, 10
f := func() int { return 7 }
ch := make(chan int)
r, w := os.Pipe(fd) // os.Pipe() returns two values
_, y, _ := coord(p) // coord() returns three values; only interested in y coordinate
Note that it's not dynamic : everything happens at compile time, the type(s) being given by the right part expression.

How to fix 'declared but not used' compiler error in this simple program?

I am trying to learn Go. I really don't understand why the compiler is saying that I am not using a variable. It seems to me that I am using the variable as an argument to Println.
My textbook states:
In this for loop i represents the current position in the array and
value is the same as x[i]
package main
import "fmt"
func main() {
x := [5]float64{ 1,2,3,4,5 }
i := 0
var total float64 = 0
for i, value := range x {
total += value
fmt.Println(i, value)
}
fmt.Println("Average:", total / float64(len(x)))
}
Output on OS X:
go run main.go
# command-line-arguments
./main.go:8: i declared and not used
Surely this fmt.Println(i, value) is using the variable i?
How to fix the compiler message?
Remove the outer i from your program:
package main
import "fmt"
func main() {
x := [5]float64{1, 2, 3, 4, 5}
var total float64 = 0
for i, value := range x {
total += value
fmt.Println(i, value)
}
fmt.Println("Average:", total/float64(len(x)))
}
Surely this fmt.Println(i, value) is using the variable i?
Yes, but the one you're defining inside the for loop. (note the :=), here:
for i, value := range x
^ ^
The outer variable i is never used.

Resources