Having a procedure with alot of blocks of nested for-loops ex. one block
could look like this
for a := 0 to x do
//Do something
for b := 0 to x do
for c := 0 to a do
//Do something
Is there any penalty in declaring all the loop variables in each for-loop like
for var a := 0 to x do
for var b := 0 to x do
for var c := 0 to a
Or should they be declared once at the top and reused?
Go with the inline. It's tidier and the compiler should optimize it.
Related
I would like to concurrently perform an operation on the elements of a slice
I am using the sync/errgroup package to handle concurrency
Here is a minimal reproduction on Go Playground https://go.dev/play/p/yBCiy8UW_80
import (
"fmt"
"golang.org/x/sync/errgroup"
)
func main() {
eg := errgroup.Group{}
input := []int{0, 1, 2}
output1 := []int{}
output2 := make([]int, len(input))
for i, n := range input {
eg.Go(func() (err error) {
output1 = append(output1, n+1)
output2[i] = n + 1
return nil
})
}
eg.Wait()
fmt.Printf("with append %+v", output1)
fmt.Println()
fmt.Printf("with make %+v", output2)
}
outputs
with append [3 3 3]
with make [0 0 3]
versus expected [1 2 3]
You have two separate issues going on here:
First, the variables in your loop are changing before each goroutine gets a chance to read them. When you have a loop like
for i, n, := range input {
// ...
}
the variables i and n live for the whole duration of the loop. When control reaches the bottom of the loop and jumps back up to the top, those variables get assigned new values. If a goroutine started in the loop is using those variables, then their value will change unpredictably. This is why you are seeing the same number show up multiple times in the output of your example. The goroutine started in the first loop iteration doesn't start executing until n has already been set to 2.
To solve this, you can do what NotX's answer shows and create new variables that are scoped to just a single iteration of the loop:
for i, n := range input {
ic, nc := i, n
// use ic and nc instead of i and n
}
Variables declared inside a loop are scoped to just a single iteration of the loop, so when the next iteration of the loop starts, entirely new variables get created, preventing the originals from changing between when the goroutine is launched and when it actually starts running.
Second you are concurrently modifying the same value from different goroutines, which isn't safe. In particular, you're using append to append to the same slice concurrently. What happens in this case is undefined and all kinds of bad things could happen.
There are two ways to deal with this. The first one you already have set up: pre-allocate an output slice with make and then have each goroutine fill in a specific position in the slice:
output := make([]int, 3)
for i, n := range input {
ic, nc := i, n
eg.Go(func() (err error) {
output[ic] = nc + 1
return nil
})
}
eg.Wait()
This works great if you know how many outputs you're going to have when you start the loop.
The other option is to use some kind of locking to control access to the output slice. sync.Mutex works great for this:
var output []int
mu sync.Mutex
for _, n := range input {
nc := n
eg.Go(func() (err error) {
mu.Lock()
defer mu.Unlock()
output = append(output, nc+1)
return nil
})
}
eg.Wait()
This works if you don't know how many outputs you have, but it doesn't guarantee anything about the order of the output - it could be in any order. If you want to put it in order, you can always do some kind of sort after all the goroutines finish.
There is no guarantee of the order when you run some go routines. So while it's okay to expect the elements 1, 2, 3, you shouldn't make any assumption about the order.
Anyway, it looks like the first eg.Go() call happens when for for loop has actually reached it's third element. This is why you only get 3, and with index access only at the 3rd position (where i=2).
If you copy your values like this, the problem is somewhat fixed:
for i, n := range input {
nc, ic := n, i
eg.Go(func() (err error) {
output1 = append(output1, nc+1)
output2[ic] = nc + 1
return nil
})
}
That said, the result looks like
with append [3 2 1]
with make [1 2 3]
for me, so the order still isn't we might have expected.
I'm no expert on the errgroup package, though, so maybe somebody else can share more information about the order of execution.
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
The documenation states:
As a consequence, redeclaration can only appear in a multi-variable short declaration. Redeclaration does not introduce a new variable; it just assigns a new value to the original.
But how does this work in for loops? See this example. It seems that the variable "nextPos", which has a scope outside the loop, actually gets redeclared inside the loop for the inner scope, and thus looses its value for each iteration. This version works though.
Let's show how it works, with these code samples:
Let's simplify your first sample, see this working sample code (1):
package main
import "fmt"
func main() {
a := 100
{
fmt.Println(a) // 100
a, b := 0, 0
fmt.Println(a, b) // 0 0
}
fmt.Println(a) // 100
}
output:
100
0 0
100
so a in a, b := 0, 0 is shadowed, this a is new variable,
this is called Variable scoping and shadowing,
and you may name it e.g. c like this code for now to show how it works (2):
package main
import "fmt"
func main() {
a := 100
{
fmt.Println(a) // 100
c, b := 0, 0
fmt.Println(c, b) // 0 0
}
fmt.Println(a) // 100
}
the output is that same as (1):
100
0 0
100
and lets simplify your next sample code (3):
package main
import "fmt"
func main() {
a := 0
b := byte(0)
{
fmt.Println(a, b) // 0 0
a, b = 1, byte(1)
fmt.Println(a, b) // 1 1
}
fmt.Println(a, b) // 1 1
}
output:
0 0
1 1
1 1
so here a and b are the same inside and outside loop.
also see: Where can we use Variable Scoping and Shadowing in Go?
and see:
What is the difference between := and = in Go?
That's how the short assignment statement := works. From the specs:
Unlike regular variable declarations, a short variable declaration may
redeclare variables provided they were originally declared earlier in
the same block (or the parameter lists if the block is the function
body) with the same type, and at least one of the non-blank variables
is new. As a consequence, redeclaration can only appear in a
multi-variable short declaration. Redeclaration does not introduce a
new variable; it just assigns a new value to the original.
Thus, in your first example, you can see that there is one new variable, namely nextB, and this re-declares nextPos as well for each loop iteration.
In the second case, both nextB and nextPos have already been declared and hence no re-declaration occurs. Also note that you are using = as the compiler would not allow := for the same reason, i.e. no new variables declared.
Or more precisely, it seems like I could do any of these three things. Is there any difference between them? Which is the best and why?
var foo []int
foo := []int{}
foo := make([]int, 0)
The difference is:
(1) is a nil slice (foo == nil).
(2) and (3) are non-nil slices (foo != nil).
The following points are true for all three statements:
The statement does not allocate memory.
The slice length is zero: len(foo) == 0.
The slice capacity is zero: cap(foo) == 0.
Playground example
Because len, cap and append work with nil slices, (1) can often be used interchangeably with (2) and (3).
Statements 2 and 3 are short variable declarations. These statements can also be written as a variable declaration with an initializer.
var foo = []int{}
var foo = make([]int, 0)
All of the options are used commonly in Go code.
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.