Golang := operator for multiple variables [duplicate] - go

This question already has answers here:
Assigning slice vs redeclaring slice in Go
(3 answers)
Multi-variable short redeclaration when inside a for loop creates a new variable?
(2 answers)
Closed 9 months ago.
I'm starting using Golang as my main programming language to do exercises in LeetCode, and I get a strange result for Golang's := operator for multiple variables.
Let me say the question 104 to get the maxDepth of a binary tree.
My first solution is to use layer traversing, and below is my code.
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func maxDepth(root *TreeNode) int {
if root == nil {
return 0
}
var s []*TreeNode
var cur *TreeNode
depth := 0
s = append(s, root)
for len(s) != 0 {
size := len(s)
depth++
for i := size; i > 0; i-- {
s, cur = s[1:], s[0]
if cur.Left != nil {
s = append(s, cur.Left)
}
if cur.Right != nil {
s = append(s, cur.Right)
}
}
}
return depth
}
The upper code snippet can pass the test. But the below code snippet can't, and I don't know why, I just can debug the for loop can't finish, and it becomes an infinite loop.
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func maxDepth(root *TreeNode) int {
if root == nil {
return 0
}
var s []*TreeNode
depth := 0
s = append(s, root)
for len(s) != 0 {
size := len(s)
depth++
for i := size; i > 0; i-- {
s, cur := s[1:], s[0]
if cur.Left != nil {
s = append(s, cur.Left)
}
if cur.Right != nil {
s = append(s, cur.Right)
}
}
}
return depth
}
I hope anyone can explain to me how Golang handles the := operators for multiple variables and at least one of which has already been declared in some other place. Also, you can put some official links which can prove your explanation.

If any new variable is on the left of := (which is required by the compiler) then all variables are redefined. This shadows previous variables in previous scopes.
In the second code sample, you create a new s and append to it, but the s defined before the for was not modified.

The assignment operator := isn't that big of a villain as you think it is. The actual culprit in you code is the scope of variable s.
In the first code snippet, the var s is defined outside the first (outer) for-loop. Inside inner for-loop it was only assigned.
var s []*TreeNode // Definition
s, cur = s[1:], s[0] // Assignment
Whereas in second code snippet, both statements are defining the same variable. In other words, the second assignment is creating a different var s whose scope is limited to second (inner) for-loop.
var s []*TreeNode // Definition
s, cur = s[1:], s[0] // Definition + Assignment
"So why does this cause a difference?", is the question. I've added example data and printing logic to your code snippets in Playground. Check by running them both.
1st Snippet
2nd Snippet
If you see outputs of both, the first logic (with only 1 definition) gives the expected output. But second logic (2 definitions) goes into an infinite loop. This is because the scope of the second s which got defined inside the for-loop starts after the second definition and ends with iteration of the same for-loop.
For understanding, let's call the s defined above for-loop as s_outer and the s defined inside for-loop as s_inner. Logically both should mean the same thing, but due to difference in scope, they act as follows:
for i := size; i > 0; i-- {
s_inner, cur := s_outer[1:], s_outer[0]
if cur.Left != nil {
s_inner = append(s_inner, cur.Left)
}
if cur.Right != nil {
s_inner = append(s_inner, cur.Right)
}
}
Since assignment statement is executed right-to-left, first the compiler checks for s at s[1:], s[0]. Since s_inner is yet to be defined, it will consider s to be s_outer. When it assigns, i.e., control is now on the left side of = sign in s, cur := s[1:], s[0], since it is a new definition as per := usage in Go, it takes new variable s_inner. But the scope of s_inner ends after the current iteration. i.e., after if cur.Right != nil {...}. And so, from 2nd iteration, the s value is taken as s_outer again and assigned to s_inner. This gets repeated, and hence, infinite loop!!
Baseline: If a variable is defined with the same name as an existing
variable but in an inner scope, it acts like a different variable
altogether.
I doubt these concepts cannot be efficiently explained using description. Moreover, this is more of a use-case than property of the := operator. So, the godoc and other pages must've explained it as definition+assignment. You face an issue, you research and you learn. That's the only mantra for these concepts.

Related

Dynamically sized Array in golang? [duplicate]

This question already has answers here:
How to implement resizable arrays in Go
(7 answers)
Closed 3 years ago.
I would like to know if there is any way by which I can create dynamically sized array to avoid runtime error in the code below.
Error:
panic: runtime error: index out of range in Go
Code:
/**
* Definition for singly-linked list.
* type ListNode struct {
* Val int
* Next *ListNode
* }
*/
func nextLargerNodes(head *ListNode) []int {
var a []int
var pha int
hNum := 0
currNode := head
pha = 0
for currNode.Next != nil {
iter := currNode.Next
hNum = currNode.Val
//phb = pha + 1
for(iter.Next != nil){
if hNum < iter.Val {
hNum = iter.Val
break
} else if hNum == iter.Val{
hNum = 0
break
}
iter = iter.Next
}
a[pha] = iter.Val
pha++
hNum = 0
currNode = currNode.Next
}
return a
}
You should use append function.
var a []int
is a slice, you can think of it as a "dynamic array". In order to add elements to it, you should use append method. In your code, you used array semantics.
a = append(a, iter.Val)
You can create your slice with a predefined number of elements if you know upfront how many elements you are going to have in your slice.
a := make([]int, 10)
this will create a slice with 10 elements in it.
Go arrays are fixed in size, but thanks to the builtin append method, we get dynamic behavior. The fact that append returns an object, really highlights the fact that a new array will be created if necessary. The growth algorithm that append uses is to double the existing capacity.
numbers := make([]int, 0)
numbers = append(numbers, 1)
numbers = append(numbers, 2)
fmt.Println(len(numbers)) // == 2

When appending value to slice, value is different from original value

consider this piece of code:
package main
import (
"fmt"
)
func main() {
fmt.Println(Part(11))
}
func Part(n int) string {
enumResult := [][]int{}
enum(n, n, []int{}, &enumResult)
fmt.Println(enumResult)
fmt.Println(40, enumResult[40])
return ""
}
var abc int = 0
func enum(n int, top int, pre []int, result *[][]int) {
var i int
if n > top {
i = top
} else {
i = n
}
for ; i > 0; i-- {
tempResult := append(pre, i)
if n-i == 0 {
/* if tempResult[0] == 3 && tempResult[1] == 3 && tempResult[2] == 3 && tempResult[3] == 2 {
tempResult = append(tempResult, 12345)
}*/
fmt.Println(abc, tempResult)
abc++
*result = append(*result, tempResult)
} else {
enum(n-i, i, tempResult, result)
}
}
}
When I run this code
I append value '[3,3,3,2]' to 'enumResult'
but If I check the value of 'enumResult' then '[3,3,3,1]' is appear
it`s index is 40 =>enumResult[40]
(other value is correct)
I don`t know why this is happening
Can you explain to me why?
The problem is indeed due to append.
There are two thing about append. First is, that append doe not necessarily copy memory. As the spec specifies:
If the capacity of s is not large enough to fit the additional values,
append allocates a new, sufficiently large underlying array that fits
both the existing slice elements and the additional values. Otherwise,
append re-uses the underlying array.
This may cause unexpected behavior if you are not clear. A playground example: https://play.golang.org/p/7A3JR-5IX8o
The second part is, that when append does copy memory, it grows the capacity of the slice. However, it does not grow it just by 1. A playground example: https://play.golang.org/p/STr9jMqORUz
How much append grows a slice is undocumented and considered an implentation details. But till Go 1.10, it follows this rule:
Go slices grow by doubling until size 1024, after which they grow by
25% each time.
Note that when enabling race-detector, this may change. The code for growing slice is located in $GOROOT/src/runtime/slice.go in growslice function.
Now back to the question. It should be clear now that your code did append from a same slice with sufficient capacity due to growth of the slice from append before. To solve it, make a new slice and copy the memory.
tempResult := make([]int,len(pre)+1)
copy(tempResult,pre)
tempResult[len(pre)] = i

For loop of two variables in Go

The following for loop in Go isn't allowed,
for i := 0, j := 1; i < 10; i++, j++ {...}
What's the correct equivalent of the for-loop of two variables below?
for (int i = 0, j = 1; i < 10; i ++ , j ++) {...}
You don't have a comma operator to join multiple statements, but you do have multiple assignment, so this works:
package main
import (
"fmt"
)
func main() {
for i, j := 0, 1; i < 10; i, j = i+1, j+1 {
fmt.Println("Hello, playground")
}
}
Although above Answer is accepted, and it fully satisfy the need. But I would like to contribute some further explanation to it.
Golang Does not support many things which could be done in simple terms. For loop is a most common example of this. The beauty of Go's For loop is that it merges many modern style of looping into one keyword.
Similarly Golang do with Multiple Variable declaration and assignment. According to above mentioned problem, We could solve multi-variable for loop with this simple tool which Golang provides us. If you want to look into further explanation, this question provide further details and way of declaring multiple variables in one statement.
Coming back to for loop, If we want to declare variable of same datatype we can declare them with this
var a,b,c string
but we use short hand in for loop so we can do this for initializing them with same value
i,j := 0,1
Different Datatypes and Different Values
and if we want to declare different type of variables and want to assign different values we can do this by separating variables names and after := different values by comma as well. for example
c,i,f,b := 'c',23423,21.3,false
Usage of Assignment Operator
Later on, we can assign values to multiple variables with the same approach.
x,y := 10.3, 2
x,y = x+10, y+1
Mixing Struct and Normal types in single statement
Even we can use struct types or pointers the same way. Here is a function to iterate Linked list which is defined as a struct
func (this *MyLinkedList) Get(index int) int {
for i,list := 0,this; list != nil; i,list = i+1,list.Next{
if(i==index){
return list.Val
}
}
return -1
}
This list is defined as
type MyLinkedList struct {
Val int
Next *MyLinkedList
}
Answering to Original Problem
Coming to the origin Question, Simply it could be done
for i, j := 0, 1; i < 10; i, j = i+1, j+1 {
fmt.Println("i,j",i,j)
}
Suppose you want to loop using two different starting index, you can do this way.
This is the example to check if string is palindrome or not.
name := "naman"
for i<len(name) && j>=0{
if string(name[i]) == string(name[j]){
i++
j--
continue
}
return false
}
return true
This way you can have different stopping conditions and conditions will not bloat in one line.
As pointed by Mr. Abdul, for iterate among two variable you can use the following construct:
var step int = 4
for row := 0; row < rowMax; row++ {
for col := 0; col < colMax; col++ {
for rIndex, cIndex := row, col; rIndex <= row+step && cIndex <= col; rIndex, cIndex = rIndex+1, cIndex+1 {
}
}
}

Remove slice element within a for

An idiomatic method to remove an element i from a slice a, preserving the order, seems to be:
a = append(a[:i], a[i+1:]...)
I was wondering which would be the best way to do it inside a loop. As I understand, it is not possible to use it inside a range for:
for i := range a { // BAD
if conditionMeets(a[i]) {
a = append(a[:i], a[i+1:]...)
}
}
However it is possible to use len(a). [EDIT: this doesn't work, see answers below]
for i := 0; i < len(a); i++ {
if conditionMeets(a[i]) {
a = append(a[:i], a[i+1:]...)
}
}
Is there a better or more idiomatic way than using len or append?
Your proposed solution is incorrect. The problem is that when you remove an element from a slice, all subsequent elements are shifted. But the loop doesn't know that you changed the underlying slice and loop variable (the index) gets incremented as usual, even though in this case it shouldn't because then you skip an element.
And if the slice contains 2 elements which are right next to each other both of which need to be removed, the second one will not be checked and will not be removed.
So if you remove an element, the loop variable has to be decremented manually! Let's see an example: remove words that start with "a":
func conditionMeets(s string) bool {
return strings.HasPrefix(s, "a")
}
Solution (try it with all other examples below on the Go Playground):
a := []string{"abc", "bbc", "aaa", "aoi", "ccc"}
for i := 0; i < len(a); i++ {
if conditionMeets(a[i]) {
a = append(a[:i], a[i+1:]...)
i--
}
}
fmt.Println(a)
Output:
[bbc ccc]
Or better: use a downward loop and so you don't need to manually decrement the variable, because in this case the shifted elements are in the "already processed" part of the slice.
a := []string{"abc", "bbc", "aaa", "aoi", "ccc"}
for i := len(a) - 1; i >= 0; i-- {
if conditionMeets(a[i]) {
a = append(a[:i], a[i+1:]...)
}
}
fmt.Println(a)
Output is the same.
Alternate for many removals
If you have to remove "many" elements, this might be slow as you have to do a lot of copy (append() does the copy). Imagine this: you have a slice with 1000 elements; just removing the first element requires copying 999 elements to the front. Also many new slice descriptors will be created: every element removal creates 2 new slice descriptors (a[:i], a[i+1:]) plus a has to be updated (the result of append()). In this case it might be more efficient to copy the non-removable elements to a new slice.
An efficient solution:
a := []string{"abc", "bbc", "aaa", "aoi", "ccc"}
b := make([]string, len(a))
copied := 0
for _, s := range(a) {
if !conditionMeets(s) {
b[copied] = s
copied++
}
}
b = b[:copied]
fmt.Println(b)
This solution allocates a slice with the same length as the source, so no new allocations (and copying) will be performed. This solution can also use the range loop. And if you want the result in a, assign the result to a: a = b[:copied].
Output is the same.
In-place alternate for many removals (and for general purposes)
We can also do the removal "in place" with a cycle, by maintaining 2 indices and assigning (copying forward) non-removable elements in the same slice.
One thing to keep in mind is that we should zero places of removed elements in order to remove references of unreachable values so the GC can do its work. This applies to other solutions as well, but only mentioned here.
Example implementation:
a := []string{"abc", "bbc", "aaa", "aoi", "ccc"}
copied := 0
for i := 0; i < len(a); i++ {
if !conditionMeets(a[i]) {
a[copied] = a[i]
copied++
}
}
for i := copied; i < len(a); i++ {
a[i] = "" // Zero places of removed elements (allow gc to do its job)
}
a = a[:copied]
fmt.Println(a)
Output is the same. Try all the examples on the Go Playground.

The Reader interface change value

I have a question about the reader interface, the definition looks like:
type Reader interface {
Read(p []byte) (n int, err error)
}
I have following code that use the reader interface:
package main
import (
"fmt"
"os"
)
// Reading files requires checking most calls for errors.
// This helper will streamline our error checks below.
func check(e error) {
if e != nil {
panic(e)
}
}
func main() {
// You'll often want more control over how and what
// parts of a file are read. For these tasks, start
// by `Open`ing a file to obtain an `os.File` value.
f, err := os.Open("configuration.ini")
check(err)
// Read some bytes from the beginning of the file.
// Allow up to 5 to be read but also note how many
// actually were read.
b1 := make([]byte, 10)
n1, err := f.Read(b1)
check(err)
fmt.Printf("%d bytes: %s\n", n1, string(b1))
f.Close()
}
As you can see the code above, b1 is defined as byte slice and it passed to the Read method as value argument. After the Read method, the b1 contains the first 10 letters from file.
What for me very confusing about the code above is, why does b1 contains suddenly values after the Read method.
In Golang, when I pass a value to the method, it will be passed as value and not as reference. To clarify, what I talking about, I made a sample application:
package main
import (
"fmt"
)
func passAsValue(p []byte) {
c := []byte("Foo")
p = c
}
func main() {
b := make([]byte, 10)
passAsValue(b)
fmt.Println(string(b))
}
After passAsValue function, b does not contain any values and that what I expected in golang, arguments will be pass as value to the function or method.
Why then, the first code snippet can change the content of the passed argument? If the Read method expects a pointer of []byte slice, then I would be agreed, but on this case not.
Everything is passed by value (by creating a copy of the value being passed).
But since slices in Go are just descriptors for a contiguous segment of an underlying array, the descriptor will be copied which will refer to the same underlying array, so if you modify the contents of the slice, the same underlying array is modified.
If you modify the slice value itself in the function, that is not reflected at the calling place, because the slice value is just a copy and the copy will be modified (not the original slice descriptor value).
If you pass a pointer, the value of the pointer is also passed by value (the pointer value will be copied), but in this case if you modify the pointed value, that will be the same as at the calling place (the copy of the pointer and the original pointer points to the same object/value).
Related blog articles:
Go Slices: usage and internals
Arrays, slices (and strings): The mechanics of 'append'
The slice header in Go contains in itself a pointer to the underlaying array.
You can read from the official blog post: https://blog.golang.org/slices
Even though the slice header is passed by value, the header includes a pointer to elements of an array, so both the original slice header and the copy of the header passed to the function describe the same array. Therefore, when the function returns, the modified elements can be seen through the original slice variable.
It is the exact same behaviour as passing a pointer in C :
#include <stdio.h>
#include <stdlib.h>
// p is passed by value ; however, this function does not modify p,
// it modifies the values pointed by p.
void read(int* p) {
int i;
for( i=0; i<10; i++) {
p[i] = i+1;
}
}
// p is passed by value, so changing p itself has no effect out
// of the function's scope
void passAsValue(int*p) {
int* c = (int*)malloc(3*sizeof(int));
c[0] = 15; // 'F' in hex is 15 ...
c[1] = 0;
c[2] = 0;
p = c;
}
int main() {
int* p = (int*)malloc(10*sizeof(int));
int i;
for( i=0; i<10; i++) {
p[i] = 0;
}
printf(" init : p[0] = %d\n", p[0]);
read(p);
printf(" after read : p[0] = %d\n", p[0]);
passAsValue(p);
printf("after passAsValue : p[0] = %d\n", p[0]);
return 0;
}
output :
// init : p[0] = 0
// after read : p[0] = 1
//after passAsValue : p[0] = 1 // <- not 15, the modification from
// // within passAsValue is not persistent
(for the record : this C program leaks the int* c array)
A Go slice contains more info than just the pointer : it is a small struct, which contains the pointer, the length, and the max capacity of the allocated array (see the link mentioned in other answers : https://blog.golang.org/slices ).
But from the code's perspective, it behaves exactly like the C pointer.

Resources