Golang learning notes - process control

Posted by rapmonkey on Fri, 11 Feb 2022 16:25:25 +0100

Elif else structure

The left brace {after the keyword if and else must be on the same line as the keyword. If you use the else if structure, the right brace} of the previous code block must be on the same line as the else if keyword. Both of these rules are enforced by the compiler.

// Illegal code

if condition{

}
else {  // invalid
}

In some cases, parentheses on both sides of conditional statements can be omitted; When the conditions are complex, you can use parentheses to make the code easier to read. If the condition is allowed, it is qualified, and & &, |, or!, You can use parentheses to raise the operation priority of an expression and improve the readability of the code.

package main
import "fmt"

func main() {
    bool1 := true
    if bool1 {
        fmt.Printf("The value is true\n")
    } else {
        fmt.Printf("The value is false\n")
    }
}

When there are break, continue, goto or return statements in the if structure, the common way to write Go code is to omit the else part. When x or y will be returned no matter which condition is met, the following writing method is generally used:

if condition {
    return x
}
return y

Get operating system information

package main

import (
	"fmt"
	"runtime"
)

func main() {
	os := runtime.GOOS
	if os == "windows"     {
		fmt.Println("windows")
	} else if os == "linux" {
		fmt.Println("linux")
	} else {
		fmt.Println("darwin")
	}
}

switch structure

switch var1 {
    case val1:
        ...
    case val2:
        ...
    default:
        ...
}

The variable var1 can be of any type, while val1 and val2 can be any value of the same type. Types are not limited to constants or integers, but must be of the same type; Or the final result is an expression of the same type. The first curly bracket {must be on the same line as the switch keyword.

You can test multiple values that may meet the criteria at the same time and separate them with commas, for example: case val1, val2, val3.

switch var1 {
    case val1, val2:
        ...
    case val3:
        ...
    default:
        ...
}

Once a branch is successfully matched, the whole switch code block will exit after executing the corresponding code, that is, you don't need to use the break statement to indicate the end.

Therefore, the program will not automatically execute the code of the next branch. If you want to continue to execute the code of subsequent branches after executing the code of each branch, you can use the fallthrough keyword to achieve the purpose.
After using the fallthrough keyword, the next case statement will be executed no matter whether it is matched successfully or not, but a fallthrough will only execute the next case. If fallthrough is used but there is no statement after it, an error will be compiled

package main

import "fmt"

func main() {
	i := 0
	switch i {
	case 0: fallthrough
	case 1:
		f1() // When i == 0, the function will also be called. If there is fallthrough here, the following case s will continue to be executed
	}
	case 2: f2()
}

func f1(){
	fmt.Println("this is f1")
}

func f2(){
	fmt.Println("this is f2")
}

The second form of switch statement is not to provide any judged value (in fact, the default is to judge whether it is true), and then test different conditions in each case branch. When the test result of any branch is true, the test result of the branch will be. This looks very much like a chained if else statement, but it provides a more readable way to write when there are many test conditions.

package main

import "fmt"

func main() {
	num := 7

	switch {
	case num < 0:
		fmt.Println("Number is negative")
	case num > 0 && num < 10:
		fmt.Println("Number is between 0 and 10")
	default:
		fmt.Println("Number is 10 or greater")
	}
}

// Print: Number is between 0 and 10

The third form of switch statement includes an initialization statement:

package main

import "fmt"

func main() {
	switch result := f1(); {
	case result < 0: fmt.Println("result < 0")
	case result > 0: fmt.Println("result > 0")
	default:
		fmt.Println("result = 0")
	}
}

func f1() int {
	return 1
}

for structure

Counter based iteration

The basic form is:
for initialization statement; Conditional statements; Modifier statement {}

package main

import "fmt"

func main() {
    for i := 0; i < 5; i++ {
        fmt.Printf("This is the %d iteration\n", i)
    }
}

Iteration based on conditional judgment

The second form of for structure is conditional judgment iteration without header (similar to while loop in other languages). The basic form is: for conditional statement {}.

You can also think that this is a for structure without initialization statements and modification statements, so;; It's redundant.

package main

import "fmt"

func main() {
    var i int = 5

    for i >= 0 {
        i = i - 1
        fmt.Printf("The variable i is now: %d\n", i)
    }
}

Wireless loop

Conditional statements can be omitted, such as I: = 0; I + + or for {} or for;; {} (;; will be removed when using gofmt): these loops are essentially infinite loops. The last form can also be rewritten as for true {}, but generally it will be written directly as for {}.

If there is no conditional statement in the head of the for loop, it will be considered that the condition is always true. Therefore, there must be relevant conditional judgment in the loop body to ensure that it will exit the loop at some time.

To exit the loop body directly, you can use the break statement or return statement to return directly.

However, there is a difference between the two. break just exits the current loop body, while the return statement returns the function in advance and does not execute subsequent code.

The classic application of infinite loop is the server, which is used to constantly wait and accept new requests.

For range structure

It can iterate over any set. The syntax is very similar to foreach statements in other languages. The form is:
for ix, val := range coll { }

It should be noted that val is always the copy of the value of the corresponding index in the set, so it is generally read-only. Any modification to val will not affect the original value in the set (if val is a pointer, a copy of the pointer will be generated, and the original value in the set can still be modified). A string is a collection of Unicode encoded characters (or rune), so you can also use it to iterate the string:

package main

import "fmt"

func main() {

	str := "Go is a beautiful language!"
	fmt.Printf("The length of str is: %d\n", len(str))
	for pos, char := range str {
		fmt.Printf("Character on position %d is: %c \n", pos, char)
	}
}

Will the following code cause an endless loop?

package main

import "fmt"

func main() {
 s := []int{1,2,3,4,5}
 for _, v:=range s {
  s =append(s, v)
  fmt.Printf("len(s)=%v\n",len(s))
 }
}

for range source code

// The loop we generate:
//   for_temp := range
//   len_temp := len(for_temp)
//   for index_temp = 0; index_temp < len_temp; index_temp++ {
//           value_temp = for_temp[index_temp]
//           index = index_temp
//           value = value_temp
//           original body
//   }

There is no dead loop. for range is actually a syntax sugar of golang. Before the loop starts, it will obtain the length of the slice len (slice), and then execute the loop of len (slice) times.

The above code will be considered by the compiler as

func main() {
 s := []int{1,2,3,4,5}
 for_temp := s
 len_temp := len(for_temp)
 for index_temp := 0; index_temp < len_temp; index_temp++ {
  value_temp := for_temp[index_temp]
  _ = index_temp
  value := value_temp
  // Here is the original body
  s =append(s, value)
  fmt.Printf("len(s)=%v\n",len(s))
 }
}

Code run output

len(s)=6
len(s)=7
len(s)=8
len(s)=9
len(s)=10

Topics: Go