Odd behavior of scopes [duplicate] - go

This question already has answers here:
My object is not updated even if I use the pointer to a type to update it
(3 answers)
Closed 3 years ago.
Consider the following minimal example:
package main
import "fmt"
type runner interface {
s1(int)
s2(int)
}
type test struct {
x1 []int
x2 []int
}
func (t test) s1(v int) {
t.x1 = append(t.x1, v)
t.s2(v)
}
func (t test) s2(v int) {
t.x2[v] = v
}
func main() {
t := test{
x1: make([]int, 0),
x2: make([]int, 10)}
for i := 0; i < 10; i++ {
t.s1(i)
}
fmt.Println(t)
}
Now if you run it, you will get a result like this:
{[] [0 1 2 3 4 5 6 7 8 9]}
meaning that the x1 array is never populated. Or actually, it is, but resets on each time s1 function exits. s2 works just fine placing items in a pre-defined array.
Does anyone know what exactly is going on here? Is it because of the scope of array amends? It seems a little counter intuitive.
P.S. I do understand that x1 is a slice, where x2 is an actual pre-defined array. My own theory goes that if you work with "slices", they can only be changed within a specific scope, not anywhere else.

Value receiver makes a copy of the type and pass it to the function.
Just make it pointer and you are good to go:
func (t *test) s1(v int) {
t.x1 = append(t.x1, v)
t.s2(v)
}
Output:
&{[0 1 2 3 4 5 6 7 8 9] [0 1 2 3 4 5 6 7 8 9]}
Code:
package main
import "fmt"
type runner interface {
s1(int)
s2(int)
}
type test struct {
x1 []int
x2 []int
}
func (t *test) s1(v int) {
t.x1 = append(t.x1, v)
t.s2(v)
}
func (t test) s2(v int) {
t.x2[v] = v
}
func main() {
t := &test{
x1: make([]int, 0),
x2: make([]int, 10)}
for i := 0; i < 10; i++ {
t.s1(i)
}
fmt.Println(t)
}

Related

Golang gives different result everytime using map for AOC 2021 Day 6 problem

I've been trying to solve Advent of Code 2021 and in day 6, I am trying this solution but the result is different everytime. What seems to be the problem? Is there any memory leakage with map?
The input file can be found here
The details of the problem can be read here
For part one it was straight-forward looping over arrays but as the number of days increases, the population grows exponentially and the time complexity grows in similar manner.
with go version go1.19.3
I have tried this:
package main
import (
"fmt"
"os"
"strconv"
"strings"
)
func getInput() []int {
var parsedData []int
rawData, _ := os.ReadFile("input.txt")
data := strings.Split(string(rawData), ",")
for _, strNum := range data {
num, _ := strconv.Atoi(strNum)
parsedData = append(parsedData, num)
}
return parsedData
}
func main() {
data := getInput()
var total int64
// create a map t0 hold the number of fish with the same timer
fishWithSameTimer := make(map[int]int64)
for _, timer := range data {
if _, ok := fishWithSameTimer[timer]; ok {
fishWithSameTimer[timer] += 1
} else {
fishWithSameTimer[timer] = 1
}
}
const days int = 18
currDay := 1
for currDay <= days {
tempFishTimerData := make(map[int]int64)
for timer, numOfFishes := range fishWithSameTimer {
if timer == 0 {
tempFishTimerData[8] = numOfFishes
tempFishTimerData[6] = numOfFishes
}else{
tempFishTimerData[timer - 1] += numOfFishes
}
}
fishWithSameTimer = tempFishTimerData
fmt.Println("Day:", currDay, fishWithSameTimer)
currDay++
}
fmt.Println(fishWithSameTimer)
for _, num := range fishWithSameTimer {
total += num
}
fmt.Println(total)
}
Can anyone help?
I hope this piece of code does the work, please add the input file reading part and print the output slice as comma separated string. You can also validate if the input has all numbers between 0 and 8.
package main
import "fmt"
func refreshTimer(x int) (int, bool) {
if x == 0 {
return 6, true
} else {
return x - 1, false
}
}
func spawnFish(i []int) []int {
var parentIntTimer []int
var childIntTimer []int
for _, d := range i {
y, c := refreshTimer(d)
parentIntTimer = append(parentIntTimer, y)
if c {
childIntTimer = append(childIntTimer, 8)
}
}
return append(parentIntTimer, childIntTimer...)
}
func main() {
initialFishes := []int{3, 4, 3, 1, 2}
var spFishes []int
noOfDays := 18
for i := 1; i <= noOfDays; i++ {
spFishes = spawnFish(initialFishes)
initialFishes = spFishes
}
fmt.Println(spFishes)
}
Output: [6 0 6 4 5 6 0 1 1 2 6 0 1 1 1 2 2 3 3 4 6 7 8 8 8 8]

Is Golang's ... syntax really just varargs?

Well, I have a simple example, although it may seem pointless, but these examples have deviated greatly from my cognition.
Can anyone tell me what happened.
I pass the elements in []int to the parameter args with the ... syntax, but when I change the formal parameter args, the []int actual parameter is changed.
I modified the ordering of args , but the ordering of []int is also affected:
package main
import (
"fmt"
)
func bubbleSort(args ...int) {
for i := 0; i < len(args); i++ {
for j := 0; j < len(args)-1; j++ {
if args[j] > args[j+1] {
args[j], args[j+1] = args[j+1], args[j]
}
}
}
}
func main() {
isle := []int{3, 6, 1, 2, 5}
bubbleSort(isle...)
fmt.Printf("%v\n", isle)
}
// [1 2 3 5 6]
What if I pass in a single parameter? Apparently []int is not affected by args:
package main
import (
"fmt"
)
func bubbleSort(args ...int) {
for i := 0; i < len(args); i++ {
for j := 0; j < len(args)-1; j++ {
if args[j] > args[j+1] {
args[j], args[j+1] = args[j+1], args[j]
}
}
}
}
func main() {
isle := []int{3, 6, 1, 2, 5}
bubbleSort(isle[0], isle[1], isle[2], isle[3], isle[4])
fmt.Printf("%v\n", isle)
}
// [3 6 1 2 5]
Golang is not about "variable safety" as Rust. So you shoud never think that slice sent to function will not be corrupted.
If you want to use variadic functions but send slices, copy it:
func myUnsafeFunc(args ...int) {
// it will change args
}
func wantToSaveSliceFunc() {
myLovingSlice := []int{1,2,3}
myUnsafeFunc(append([]int(nil), slice...)...)
}
https://freshman.tech/snippets/go/copy-slices/
It's not confusing if you remember that spread operator ... is just a shugar .
Do not use it in your own API without serious reasone (as in fmt.Printf(...) for example) prefer use of explicit x []type notation over x ...type:
func myUnsafeFunc(args []int) {
// it will change args
}
func wantToSaveSliceFunc() {
myLovingSlice := []int{1,2,3}
myUnsafeFunc(append([]int(nil), slice...))
}
it still requires copying of slice, but it's much clear by semantics

Incorrect values inside goroutines when looping

I have read through CommonMistakes as well as run my code through the -race flag, but I can't seem to pinpoint what is wrong here:
package main
import (
"fmt"
)
func main() {
i := 1
totalHashFields := 6
for i <= totalHashFields {
Combinations(totalHashFields, i, func(c []int) {
fmt.Println("Outside goroutine:", c)
go func(c []int) {
fmt.Println("Inside goroutine:", c)
}(c)
})
i++
}
}
func Combinations(n, m int, emit func([]int)) {
s := make([]int, m)
last := m - 1
var rc func(int, int)
rc = func(i, next int) {
for j := next; j < n; j++ {
s[i] = j
if i == last {
emit(s)
} else {
rc(i+1, j+1)
}
}
return
}
rc(0, 0)
}
(The Combinations function is a combinations algo for those interested)
Here is some of the output from fmt.Println:
Outside goroutine: [0 1 4]
Inside goroutine: [5 5 5]
Outside goroutine: [0 1 2 3 4 5]
Inside goroutine: [5 5 5 5 5 5]
Basically, even though I'm passing c as a parameter to my anonymous go function, the value is consistently different to the value outside of this scope. In the output above, I expected the 2 "Inside" values to also be [0 1 4] and [0 1 2 3 4 5], respectfully.
The problem is that you goroutines all work on distinc int slices but these share a common backing array: After completing Combinations the slice s will be full of 5s. Your c in main shares the underlying backing array with s.
But your goroutines do not start executing until Combinations is done so once they do start, the will see the final value of s which is just 5s.
Here it does not help to pass in the slice like you did as this makes a proper copy of c but not of the backing array.
Try
Combinations(totalHashFields, i, func(c []int) {
fmt.Println("Outside goroutine:", c)
cpy := make([]int, len(c))
copy(cpy, c)
go func(c []int) {
fmt.Println("Inside goroutine:", c)
}(cpy)
})
to make a "deep copy" of c.

I need to assign a variable to itself when iterating for the closure context to keep the correct value

Without i := i, I get an incorrect result (3, 3, 5, 9, 7, 15). With it, I get (0, 0, 3, 3, 6, 10), which is correct. Removing the assignment is similar to getting the value of i at the end of the loop. Why?
package main
import "fmt"
type Handler interface {
Handle(v int)
}
type Elem struct {
Handler Handler
}
var elems []*Elem
type handlerFunc func(v int)
func (h handlerFunc) Handle(v int) { h(v) }
func main() {
newElem := func(fn handlerFunc) {
elem := &Elem{Handler: handlerFunc(fn)}
elems = append(elems, elem)
}
for i := 0; i < 3; i++ {
i := i // *** Why? ***
newElem(func(v int) { fmt.Printf("%d, ", i+v) })
newElem(func(v int) { fmt.Printf("%d, ", i*v) })
}
for n, e := range elems {
if e.Handler != nil {
e.Handler.Handle(n)
}
}
fmt.Printf("\n")
}
The easiest way to visualize what is going on is to change the output of your functions to:
newElem(func(v int) { fmt.Printf("plus %d - %d+%d\n", i+v, i, v) })
newElem(func(v int) { fmt.Printf("times %d - %d*%d\n", i*v, i , v) })
With this change the output becomes:
plus 3 - 3+0
times 3 - 3*1
plus 5 - 3+2
times 9 - 3*3
plus 7 - 3+4
times 15 - 3*5
So, as you can see, i is 3 in all cases. This is because you are creating closures around the i variable, so the functions will use the current value of i when they run, which is 3 by the time the functions actually use i.
You can again see this if you change your code like the following:
http://play.golang.org/p/FRhr0n2oi7
The reason the assignment i := i inside your loop fixes the problem is because you are creating a new variable i inside the scope of your loop which is still closed over by the functions, but doesn't ever change. Each new iteration of the loop creates a new i so no previous i changes value.
Although this document describes a common mistake when dealing with goroutines and closures, it should shed a bit more light on the issue and potential solutions.
https://github.com/golang/go/wiki/CommonMistakes

What is the correct way to find the min between two integers in Go?

I imported the math library in my program, and I was trying to find the minimum of three numbers in the following way:
v1[j+1] = math.Min(v1[j]+1, math.Min(v0[j+1]+1, v0[j]+cost))
where v1 is declared as:
t := "stackoverflow"
v1 := make([]int, len(t)+1)
However, when I run my program I get the following error:
./levenshtein_distance.go:36: cannot use int(v0[j + 1] + 1) (type int) as type float64 in argument to math.Min
I thought it was weird because I have another program where I write
fmt.Println(math.Min(2,3))
and that program outputs 2 without complaining.
so I ended up casting the values as float64, so that math.Min could work:
v1[j+1] = math.Min(float64(v1[j]+1), math.Min(float64(v0[j+1]+1), float64(v0[j]+cost)))
With this approach, I got the following error:
./levenshtein_distance.go:36: cannot use math.Min(int(v1[j] + 1), math.Min(int(v0[j + 1] + 1), int(v0[j] + cost))) (type float64) as type int in assignment
so to get rid of the problem, I just casted the result back to int
I thought this was extremely inefficient and hard to read:
v1[j+1] = int(math.Min(float64(v1[j]+1), math.Min(float64(v0[j+1]+1), float64(v0[j]+cost))))
I also wrote a small minInt function, but I think this should be unnecessary because the other programs that make use of math.Min work just fine when taking integers, so I concluded this has to be a problem of my program and not the library per se.
Is there anything that I'm doing terrible wrong?
Here's a program that you can use to reproduce the issues above, line 36 specifically:
package main
import (
"math"
)
func main() {
LevenshteinDistance("stackoverflow", "stackexchange")
}
func LevenshteinDistance(s string, t string) int {
if s == t {
return 0
}
if len(s) == 0 {
return len(t)
}
if len(t) == 0 {
return len(s)
}
v0 := make([]int, len(t)+1)
v1 := make([]int, len(t)+1)
for i := 0; i < len(v0); i++ {
v0[i] = i
}
for i := 0; i < len(s); i++ {
v1[0] = i + 1
for j := 0; j < len(t); j++ {
cost := 0
if s[i] != t[j] {
cost = 1
}
v1[j+1] = int(math.Min(float64(v1[j]+1), math.Min(float64(v0[j+1]+1), float64(v0[j]+cost))))
}
for j := 0; j < len(v0); j++ {
v0[j] = v1[j]
}
}
return v1[len(t)]
}
Until Go 1.18 a one-off function was the standard way; for example, the stdlib's sort.go does it near the top of the file:
func min(a, b int) int {
if a < b {
return a
}
return b
}
You might still want or need to use this approach so your code works on Go versions below 1.18!
Starting with Go 1.18, you can write a generic min function which is just as efficient at run time as the hand-coded single-type version, but works with any type with < and > operators:
func min[T constraints.Ordered](a, b T) T {
if a < b {
return a
}
return b
}
func main() {
fmt.Println(min(1, 2))
fmt.Println(min(1.5, 2.5))
fmt.Println(min("Hello", "世界"))
}
There's been discussion of updating the stdlib to add generic versions of existing functions, but if that happens it won't be until a later version.
math.Min(2, 3) happened to work because numeric constants in Go are untyped. Beware of treating float64s as a universal number type in general, though, since integers above 2^53 will get rounded if converted to float64.
There is no built-in min or max function for integers, but it’s simple to write your own. Thanks to support for variadic functions we can even compare more integers with just one call:
func MinOf(vars ...int) int {
min := vars[0]
for _, i := range vars {
if min > i {
min = i
}
}
return min
}
Usage:
MinOf(3, 9, 6, 2)
Similarly here is the max function:
func MaxOf(vars ...int) int {
max := vars[0]
for _, i := range vars {
if max < i {
max = i
}
}
return max
}
For example,
package main
import "fmt"
func min(x, y int) int {
if x < y {
return x
}
return y
}
func main() {
t := "stackoverflow"
v0 := make([]int, len(t)+1)
v1 := make([]int, len(t)+1)
cost := 1
j := 0
v1[j+1] = min(v1[j]+1, min(v0[j+1]+1, v0[j]+cost))
fmt.Println(v1[j+1])
}
Output:
1
Though the question is quite old, maybe my package imath can be helpful for someone who does not like reinventing a bicycle. There are few functions, finding minimal of two integers: ix.Min (for int), i8.Min (for int8), ux.Min (for uint) and so on. The package can be obtained with go get, imported in your project by URL and functions referred as typeabbreviation.FuncName, for example:
package main
import (
"fmt"
"<Full URL>/go-imath/ix"
)
func main() {
a, b := 45, -42
fmt.Println(ix.Min(a, b)) // Output: -42
}
As the accepted answer states, with the introduction of generics in go 1.18 it's now possible to write a generic function that provides min/max for different numeric types (there is not one built into the language). And with variadic arguments we can support comparing 2 elements or a longer list of elements.
func Min[T constraints.Ordered](args ...T) T {
min := args[0]
for _, x := range args {
if x < min {
min = x
}
}
return min
}
func Max[T constraints.Ordered](args ...T) T {
max := args[0]
for _, x := range args {
if x > max {
max = x
}
}
return max
}
example calls:
Max(1, 2) // 2
Max(4, 5, 3, 1, 2) // 5
Could use https://github.com/pkg/math:
import (
"fmt"
"github.com/pkg/math"
)
func main() {
a, b := 45, -42
fmt.Println(math.Min(a, b)) // Output: -42
}
Since the issue has already been resolved, I would like to add a few words. Always remember that the math package in Golang operates on float64. You can use type conversion to cast int into a float64. Keep in mind to account for type ranges. For example, you cannot fit a float64 into an int16 if the number exceeds the limit for int16 which is 32767. Last but not least, if you convert a float into an int in Golang, the decimal points get truncated without any rounding.
If you want the minimum of a set of N integers you can use (assuming N > 0):
import "sort"
func min(set []int) int {
sort.Slice(set, func(i, j int) bool {
return set[i] < set[j]
})
return set[0]
}
Where the second argument to min function is your less function, that is, the function that decides when an element i of the passed slice is less than an element j
Check it out here in Go Playground: https://go.dev/play/p/lyQYlkwKrsA

Resources