data type
1. Value type: variables store values directly. Memory is usually allocated in the stack. The stack will be released after the function is called
Basic data types: int series, float series, bool, string, array and struct
Int series (int(32 or 64), int8, int16, int32, int64, and uint(32 or 64), uint8, uint16, uint32, uint64)
Float series (flaot23, float64)
2. Reference type: variables directly store an address, and only the space corresponding to this address can really store data values. Memory is usually allocated on the heap. When no variable references this address, the data space corresponding to this address becomes garbage and is recycled by GC
Pointer, slice slice, map, pipe chan, interface, etc
Basic data type to string
package main import ( "fmt" "strconv" ) //Basic data type to string func main() { var num1 int = 99 var num2 float64 = 23.345 var b bool = true var myChar byte = 'm' var str string /* Mode 1: use the first mode FMT Sprintf method func Sprintf(format string, a ...interface{}) string Sprintf Generates a formatted string based on the format parameter and returns the string. */ str = fmt.Sprintf("%d", num1) fmt.Printf("str type %T str=%q\n", str, str) str = fmt.Sprintf("%f", num2) fmt.Printf("str type %T str=%q\n", str, str) str = fmt.Sprintf("%t", b) fmt.Printf("str type %T str=%q\n", str, str) str = fmt.Sprintf("%c", myChar) fmt.Printf("str type %T str=%q\n", str, str) /* Method 2: use the FormatXxx function in the strconv package func FormatBool(b bool) string func FormatInt(i int64, base int) string func FormatUint(i uint64, base int) string func FormatFloat(f float64, fmt byte, prec, bitSize int) string func Atoi(s string) (i int, err error) func Itoa(i int) string */ var num3 int = 99 var num4 float64 = 23.345 var b2 bool = true str = strconv.FormatInt(int64(num3), 10) fmt.Printf("str type %T str=%q\n", str, str) //Note: 'f' format 10: it means 10 decimal places are reserved. 64: it means that the decimal place is float64 str = strconv.FormatFloat(num4, 'f', 10, 64) fmt.Printf("str type %T str=%q\n", str, str) str = strconv.FormatBool(b2) fmt.Printf("str type %T str=%q\n", str, str) //The function Itoa in strconv converts int to string var num5 int = 456 str = strconv.Itoa(num5) fmt.Printf("str type %T str=%q\n", str, str) }
string to basic data type
package main import ( "fmt" "strconv" ) /* Use the functions in strconv package to convert string to basic data type func ParseBool(str string) (value bool, err error) func ParseInt(s string, base int, bitSize int) (i int64, err error) func ParseUint(s string, base int, bitSize int) (n uint64, err error) func ParseFloat(s string, bitSize int) (f float64, err error) */ func main() { var str string = "true" var b bool //The ParseXxx function will return two values. We only want value bool, not err, so we use_ ignore b, _ = strconv.ParseBool(str) fmt.Printf("b type %T b=%v\n", b, b) var str2 string = "123456789" var n1 int64 n1, _ = strconv.ParseInt(str2, 10, 64) fmt.Printf("n1 type %T n1=%v\n", n1, n1) var str3 string = "123.456" var n2 float64 n2, _ = strconv.ParseFloat(str3, 64) fmt.Printf("n2 type %T n2=%v\n", n2, n2) //Note to the above: the returned is int64 float64, which can be converted to int32 float32 //Note: when converting string to basic data type: ensure that string can be converted to corresponding basic data type var str4 string = "hello" var n3 int64 n3, _ = strconv.ParseInt(str4, 10, 64) fmt.Printf("n3 type %T n3=%v\n", n3, n3) //No error will be reported, but it cannot be converted. The default value is n3=0 }
array
Definition of array:
var array name [array size] data type
How to initialize an array
var array [3]int = [3]int{1,2,3} array := [4][2]int{{10,11},{20,21},{30,31},{40,41}} var array = [3]{1,2,3} array := [...]string{} //Specify a specific value for a specific subscript var array = [3]string{1:"tom",2:jack} //... Instead of the length of the array, go language determines the length of the array according to the elements of the array during initialization var array = [...]int{1,2,3}
Traversal of array
1,Conventional practice var arr = [...]arr{1,2,3,4} for i := 0;i < len(arr); i++ { fmt.Printf("%c \n",arr[i]) } 2,for-range Structure traversal var arr = [...]int{1,2,3,4} for _,v := range arr { fmt.Printf("%v \n",v) }
Precautions for using arrays:
- [10]int is not of the same type as [30]int
- After the array is created, the length is determined
- The address of the first element of the array is the first address of the array
- The address interval of each element of the array is determined according to the type of the array
2D array:
Two dimensional array traversal:
var arr2 [2][3]int for i := 0; i < len(arr2); i++ { for j := 0; j < len(arr2[0]); j++ { arr2[i][j] = i * j } } for i, v := range arr2 { for j, value := range v { fmt.Printf("arr2[%v][%v]=%v\t", i, j, value) } fmt.Println() }
section
A slice is a reference to an array,
The length of slices can vary, so slices are a dynamic array
Definition of slice:
var slice name [] type
var intArr [5]int = [...]int{1,22,3,4,5} slice := intArr[1:3] //Slice slice refers to the intArr array with the initial subscript of 1 and the final subscript of 3 (but excluding 3) fmt.Println("intArr",intArr)fmt.Println("intArr", intArr) fmt.Println("slice The element is", slice) fmt.Println("slice The number of elements is", len(slice)) fmt.Println("slice The capacity is", cap(slice)) fmt.Printf("%T", slice) //[]int Output: intArr [1 22 3 4 5] slice The element is [22 3] slice The number of elements is 2 slice The capacity of is 4 slice[0] = 100 fmt.Println("intArr", intArr) Output: intArr [1 100 3 4 5]
Understanding of slice memory:
-
slice is indeed a reference type
-
slice is actually a data structure (struct structure) from the bottom
type slice struct{ ptr *[Array length]data type len int //length cap int //capacity }
-
Slice refers to an array. If slice is modified, the referenced array will also be modified
Use of slices
-
Define a slice, and then use the slice to reference an array that has been created, as shown above
-
Create slices with make
Basic syntax: var sliceName []type = make([]type,len,[cap])
//sliceName := make([]type,len,cap)
Type is the data type, len size, and cap specifies the capacity of the slice. If cap is allocated, cap > = len
-
Define a slice and directly specify a specific array. The principle is similar to the make method
var slice []int = [...]int{1,2,3}
Precautions for slicing:
- If no value is assigned to each element of the slice, the default value is used
- The array corresponding to the slice created by make is maintained by the bottom layer of make and is invisible to the outside, that is, each element can only be accessed through slice
- The difference between method 1 and method 2 of creating slices: Method 1 refers to an array directly, which exists in advance and is visible to programmers. Method 2 is to create slices through make, and make will also create an array. The slices are maintained at the bottom, which is invisible to programmers
Traversal of slices
- Loop routine traversal
- For range structure traversal slice
Considerations and details of slicing
-
During slice initialization, var slice = arr[startIndex:endIndex]
Note: get the element with the index of endIndex from the ARR array with the index of startIndex (excluding arr[endIndex])
var slice = arr[0:end] can be abbreviated as var slice = arr[:end]
var slice = arr[start:len(arr)] can be abbreviated as var slice = arr[start:]
var slice = arr[0:len(arr)] can be abbreviated as var slice = arr [:]
-
After the slice is defined, it cannot be used because it is empty and needs to be referenced to an array or make a space for the slice
-
Slicing can continue slicing
slice2 := slice[1,2]
-
Using the append built-in function, you can dynamically append slices
func append(slice []Type, elems ...Type) []Type Built in function append Append the element to the end of the slice. If it has enough capacity, its target will be re sliced to accommodate new elements. Otherwise, a new basic array will be allocated. append Returns the updated slice, so the appended result must be stored.
var slice []int = [...]int{1,2,3} slice = append(slice,400,500,600) slice = append(slice,slice2)
- The underlying principle of slice append operation is to expand the capacity of the array, which is invisible to the programmer
-
Slice is a reference data type. When passing, it follows the reference passing mechanism
func main() { var arr [5]int = [5]int{11, 22, 33, 44, 55} var slice = arr[:] slice2 := slice slice2[0] = 666 fmt.Println(arr) fmt.Println(slice) fmt.Println(slice2) } Output result: [666 22 33 44 55] [666 22 33 44 55] [666 22 33 44 55]
string and slice
- The bottom layer of string is a byte array, so string can also be sliced
- String cannot be changed, that is, the string cannot be modified by str[0] = 'z'
- If you need to modify the string, you can convert the string to [] byte or [] run - > Modify - > re convert to string
map
Map is a key value data structure, also known as a field or associative array. Similar to the collection of other programming languages, it is often used in programming. In go, map is implemented with hash table at the bottom
Mapping is a data structure used to store a series of unordered key value pairs. Mapping stores values based on keys. map can quickly retrieve data based on key. A key, like an index, points to the value associated with the key
Basic grammar
var map variable name map[keytype]valuetype
such as: var age map[string]int
Var users map [string] map [string] string -- > value is another map
- What type of key can it be?
In golang, the key of map can be bool, number, string, pointer, channel, or interface, structure and array containing the previous types
Usually, the key is int or string
Note: slice, map and function are not allowed because they cannot be judged by = =
After the map declaration, memory will not be allocated. make is required for initialization, and memory can be assigned and used only after allocation
-
The type selection of valuetype is basically the same as that of key
Usually: number (integer, floating-point number), string, map, struct
func main() { //Declaration and precautions of map var a map[string]string //Before using the map, you need to make and allocate memory space to the map a = make(map[string]string, 10) a["no1"] = "Liu Bei" a["no2"] = "Fei Zhang" a["no1"] = "Cao Cao" a["no3"] = "Guan Yu" fmt.Println(a) }
Description of the above code:
- make a map before using it
- The key of map cannot be repeated. If it is repeated, the last key value shall prevail
- The value of a map can be the same
- The key value of map is out of order. The reason for the disorder is that the implementation of map uses hash table
- make built-in function tree
func make func make(Type, size IntegerType) Type Built in function make Allocates and initializes an object of type slice, map, or channel. Its first argument is a type, not a value. make The return type of is the same as its parameter, not a pointer to it. The specific results depend on the specific type: section: size Its length is specified. The capacity of the slice is equal to its length. Slicing supports the second integer argument, which can be used to specify different capacities; It must not be less than its length, so make([]int, 0, 10) A slice with a length of 0 and a capacity of 10 is allocated. Mapping: the creation of the initial assignment depends on size,But the resulting mapping length is 0. size It can be omitted, in which case one will be assigned Small starting size. Channel: the cache of the channel is initialized according to the specified cache capacity. if size If it is zero or omitted, the channel is uncached.
How to use map:
1: Declare first, then make, then assign
2: make the statement directly
3: Declare direct assignment
//The first method is to declare first, then make, and then assign values //Declaration and precautions of map var a map[string]string //Before using the map, you need to make and allocate memory space to the map a = make(map[string]string, 10) a["no1"] = "Liu Bei" a["no2"] = "Fei Zhang" a["no1"] = "Cao Cao" a["no3"] = "Guan Yu" fmt.Println(a) //The second way is to make the declaration directly and then assign the value cities := make(map[string]string) cities["no1"] = "Beijing" cities["no2"] = "Shanghai" cities["no3"] = "Tianjin" cities["no4"] = "Nanyang" fmt.Println(cities) //The third way: declare direct assignment heroes := map[string]string{ "hero1": "Song Jiang", "hero2": "Li Kui", } heroes["hero3"] = "Lin Chong" fmt.Println(heroes)
example:
//Each student has 3 names and gender information stus := make(map[string]map[string]string) stus["stu01"] = make(map[string]string, 3) stus["stu01"]["name"] = "tom" stus["stu01"]["sex"] = "male" stus["stu01"]["address"] = "Zhengzhou, Henan" stus["stu02"] = make(map[string]string, 3) stus["stu02"]["name"] = "mary" stus["stu02"]["sex"] = "female" stus["stu02"]["address"] = "Pudong, Shanghai" fmt.Println(stus)
Common operations of map
map additions and updates:
- map ["key"] = value / / if the key does not exist, it is added. If the key exists, it is modified
map deletion:
- delete(map, "key"), delete is a built-in function. If a key exists, the key value will be deleted. If it does not exist, no operation will be performed, but no error will be reported
func delete(m map[Type]Type1, key Type) Built in function delete Deletes the element from the map according to the specified key. if m by nil Or no such element, delete Do not operate.
Details:
If we want to delete all the keys in the map, there is no special method to delete them at one time. We can traverse the keys and delete them one by one, or map = make(...) and make a new one, so that the original one becomes garbage and is recycled by GC
stus = make(map[string]map[string]string)
map lookup
Note: if stu02 exists in the stus map, findRes will return true, otherwise false
val, findRes := stus["stu02"] if findRes { fmt.Println("eureka, val=", val) } else { fmt.Println("No, stu02 this key") } //Yes, val= map[address: Pudong, Shanghai name:mary sex: female]
map traversal
map traversal uses for range
cities := make(map[string]string) cities["no1"] = "Beijing" cities["no2"] = "Shanghai" cities["no3"] = "Tianjin" cities["no4"] = "Nanyang" fmt.Println(cities) for k,v := range cities { fmt.Printf("k=%v v=%v",k,v) } stus := make(map[string]map[string]string) stus["stu01"] = make(map[string]string, 3) stus["stu01"]["name"] = "tom" stus["stu01"]["sex"] = "male" stus["stu01"]["address"] = "Zhengzhou, Henan" stus["stu02"] = make(map[string]string, 3) stus["stu02"]["name"] = "mary" stus["stu02"]["sex"] = "female" stus["stu02"]["address"] = "Pudong, Shanghai" for k1,v1 := range stus{ fmt.Println("k1=",k1) for k2,v2 := range v1{ fmt.Printf("\tk2=%v v2=%v",k2,v2) } fmt.Println() }
map length
len(keyType)
func len(v Type) int Built in function len return v Depending on the type: Array: v Number of elements in Array pointer:*v Number of elements in( v by nil Time panic) Slice, map: v The number of elements in the; if v by nil,len(v)Is zero character string: v Number of bytes in Channel: the number of queued (unread) elements in the channel cache; if v by nil,len(v)Is zero
map slice
If the data type of slice is map, we call it slice of map. In this way, the number of maps can change dynamically
func main() { //Case: use a map to record monster's information name and age, and the number of monsters can increase dynamically var monsters []map[string]string monsters = make([]map[string]string, 2) //Prepare to put in two monsters if monsters[0] == nil { monsters[0] = make(map[string]string, 2) monsters[0]["name"] = "Ox demon king" monsters[0]["age"] = "100" } if monsters[1] == nil { monsters[1] = make(map[string]string, 2) monsters[1]["name"] = "Red boy" monsters[1]["age"] = "20" } //The following is wrong /*if monsters[2] == nil { //panic: runtime error: index out of range [2] with length 2 monsters[2] = make(map[string]string, 2) monsters[2]["name"] = "Jade Rabbit“ monsters[2]["age"] = "18" }*/ //Here we need to use the append function to slice, which can dynamically add monster //1. First define a monster learning newMonster := map[string]string{ "name": "Jade Hare", "age": "18", } monsters = append(monsters, newMonster) fmt.Println(monsters) }
map sorting
1. There is no special method in golang to sort map s
2. Maps in golang are unordered by default and are not stored in the order of addition
3. Sorting in golang is to sort the keys first, and then traverse the output according to the key value
func main() { map1 := make(map[int]int) map1[10] = 100 map1[2] = 44 map1[4] = 66 map1[6] = 88 map1[3] = 99 //1. First put the key of the map into the slice var keys []int for k, _ := range map1 { keys = append(keys, k) } //2. Sort keys slices sort.Ints(keys) fmt.Println(keys) //3. Output map1 incrementally according to the key for _, k := range keys { fmt.Printf("map1[%v]=%v\n", k, map1[k]) } }
Usage details of map
-
Map is a reference type. It follows the reference type passing mechanism. After a function receives a map, it will directly modify the original map
func modify(map1 map[int]int) { map1[10] = 666 } func main() { map1 := make(map[int]int) map1[10] = 100 map1[2] = 44 map1[4] = 66 map1[6] = 88 map1[3] = 99 modify(map1) fmt.Printn(mp1) }
-
After the capacity of the map is reached, adding elements to the map will automatically expand the capacity without panic, that is, the map can dynamically increase key value pairs
-
The value of map often uses struct type, which is more suitable for managing complex data (better than the previous value is a map). For example, value is the Student structure
type Stu struct { Name string Age int Address string } func main() { students := make(map[string]Stu, 10) stu1 := Stu{"tom", 20, "Pudong, Shanghai"} stu2 := Stu{"marry", 18, "Beijing"} students["no1"] = stu1 students["no2"] = stu2 fmt.Println(students) //Traverse students for k, v := range students { fmt.Printf("The student's number is%v\n", k) fmt.Printf("What is the student's name%v\n", v.Name) fmt.Printf("What is the age of the student%v\n", v.Age) fmt.Printf("What is the student's address%v\n", v.Address) fmt.Println() } }
identifier
Naming rules
-
It consists of 26 English letters in case, 0-9_ form
-
Number cannot start
-
golang is strictly case sensitive
-
The identifier cannot contain spaces
-
Underline "" Itself is a special identifier in go, which is called null identifier. It can represent any other identifier, but its corresponding value will be ignored (for example, ignoring a return value). Therefore, it can only be used as a placeholder, not as an identifier alone (mixed with alphanumeric).
-
System reserved keywords cannot be used as identifiers. (there are 25)
break default func interface select case defer go map struct chan else goto package switch const fallthrough if range type continue for import return var
Predefined identifier
In addition to reserved keywords, GO also provides 36 predetermined identifiers, including basic data types and system embedded functions
append bool byte cap close complex complex64 complex128 uint16 copy false float32 float64 imag int int8 int16 uint32 int32 int64 iota len make new nil panic unit64 print println real recover string true uint uint8 uintprt
hello //ok hello12 //ok 1hello //error h-b //error x h //error int //ok, grammar is ok, but don't use it like this float32 //ok, grammar is ok, but don't use it like this _ //error Abc //ok
Identifier naming convention
-
Package name: keep the package name and directory consistent. Try to use meaningful package names, which are short and meaningful. Don't conflict with the standard library, such as fmt. It is recommended to use lowercase letters for package names.
-
Variable name, function name and constant name: hump method is adopted
eg: var stuName string = "tom" form: xxxyyyzz
-
If the variable name, function name and constant name are capitalized, they can be accessed by other packages; If the initial is lowercase, it can only be used in this package (Understanding: the initial uppercase is public and the initial lowercase is private; there are no public, private and other keywords in golang)
go operator priority
From low to high
classification | operator | Associativity |
---|---|---|
suffix | () [] -> . ++ – | Left to right |
Monocular | + - ! ~ (type) * & sizeof | Right to left |
multiplication | * / % | Left to right |
addition | + - | Left to right |
displacement | << >> | Left to right |
relationship | < <= > >= | Left to right |
Equality (relation) | == != | Left to right |
Bitwise AND | & | Left to right |
Bitwise XOR | ^ | Left to right |
Bitwise OR | | | Left to right |
Logical AND | && | Left to right |
Logical OR | || | Left to right |
Assignment Operators | = += -= *= /= %= >>= <<= &= ^= |= | Right to left |
comma | , | Left to right |
go process control
Conditional statement
If judgment, if else judgment, else judgment
Initial sub statement: if statement can have a sub statement, which is used to initialize local variables. In addition, since the value of a is defined in the if code block, it cannot be called outside the code block.
func main(){ if a := 10; a < 20 { fmt.Printf("a Less than 10") }else{ fmt.Printf("a The value of is:%d\n",a) } } //Note that else cannot wrap lines //Else in else if is not required
Select statement
- Switch statements (expression switch, type switch), which also have initialization statements, can only have one sentence after the switch keyword
- select statement, which is used to cooperate with the read-write operation of channels and the concurrent read-write operation of multiple channels
Precautions and details of switch use:
- case/switch is followed by an expression (that is, constant value, variable and a function with return value can all be used)
- The data type of the value of each expression after case must be consistent with the expression data type of switch
- case can be followed by multiple expressions, separated by commas
- If the expression after case is a constant value (literal), it must not be repeated
- There is no need to bring break after the case. After the program matches a case, it will execute the corresponding code block, and then exit the switch. If none of them match, it will execute default
- The default statement is not required. (even if they don't match)
- The switch can also be used without an expression, similar to the if else branch
- You can also directly declare / define a variable after switch, ending with a semicolon. Not recommended
- Switch penetration -fallthrough. If fallthrough is added after the case statement block, the next case will continue to be executed, which is also called switch penetration
var num int = 10 switch num { case 10: fmt.Printf("ok1") fallthrough //By default, it can only penetrate one layer, and case 20 can be executed directly without judgment case 20: fmt.Printf("ok2") case 30: fmt.Printf("ok3") default: fmt.Printf("No match found") } Output: ok1 ok2
- Type Switch: the switch statement can also be used for Type Switch to determine the type of variable actually pointed to in an interface variable
package main import "fmt" func main(){ grade := "E" marks := 90 //1. Expression switch //Similar to writing in other languages //Generally, it is not written in this way. It is not easy to expand the selection statement. If the score is 100,91, D will also be returned switch marks { case 90 : grade = "A" case 80 : grade = "B" case 60,70 : grade = "C" default: grade = "D" } fmt.Printf("Your grades are:%s\n",grade) //Complete switch expression switch { //switch is not followed by an expression case marks >= 90 : grade = "A" case marks >= 80 : grade = "B" case marks >= 70: grade = "C" case marks >= 60: grade = "D" default: grade = "E" } switch { //Switch is not followed by an expression, and switch is not followed by default case grade == "A": fmt.Printf("Excellent results!\n") case grade == "B": fmt.Printf("Good performance!\n") case grade == "C",grade == "D": fmt.Printf("make persistent efforts!\n") case grade == "A": fmt.Printf("The result is unqualified!\n") } fmt.Printf("Your grade is%s\n",grade) }
package main import "fmt" var x interface{}//Empty interface func main(){ x = 1 //Modify the value of x to see the change of the returned result switch i := x.(type){ //The expression here has only one initialization sub statement case nil: fmt.Printf("Here is nil,x The type of is%T",i) case int: fmt.Printf("Here is int,x The type of is%T",i) case float64: fmt.Printf("Here is float64,x The type of is%T",i) case bool: fmt.Printf("Here is bool,x The type of is%T",i) case string: fmt.Printf("Here is string,x The type of is%T",i) default: fmt.Printf("unknown type") } }
Circular statement
In go language, the keyword of circular statement is for, and there is no while keyword. Three sub statements after the for statement: initialization sub statement, condition sub statement and post sub statement. (the order cannot be reversed. The conditional sub statement is required. The conditional sub statement will return a bool value, true will execute the code block, and false will jump out of the loop.
- > range sub statement. Each for statement can use a special range sub statement, which is similar to the iterator. It can be used to query each element in the array or slice value, poll each character of the string, array, slice, and each key value pair in the dictionary value, and even read the element in a channel type value
for recycling notes and details:
-
A loop condition is an expression that returns a Boolean value
-
The second way to use the for loop
for Cyclic judgment condition { //Circular execution statement } //Write variable initialization and variable iteration to other locations eg: j := 1 for j <= 10{ fmt.Printf("Hello,",j) j++ }
-
The third way to use the for loop
for { //Circular execution statement } eg: k := 1 for { if k <= 10 { fmt.Println("ok",k) }else{ break //break is to jump out of the for loop } k++ }
-
Golang provides for range, which can easily traverse strings and arrays
For the for range traversal mode, it is traversed in character mode. Therefore, if the string has Chinese, it is ok
//String traversal mode 1: traditional mode var str string = "hello,world" for i := 0;i < len(str); i++ { fmt.Printf("%c \n",str[i]) } //String traversal mode 2: for - range str = "abc~ok" for index,val := range str{ fmt.Printf("index=%d,val=%c \n",index,val) } /* //The above code is written in this way to understand the range clause, but it reduces the readability of the code and is not easy to manage the subsequent cyclic code blocks for index,val := range "abc~ok"{ fmt.Printf("index=%d,val=%c \n",index,val) } */ for _,char := range str { //Ignore the first value, ignore index fmt.Println(char) } for index := range str { //Ignore the second value fmt.Println(index) } for range str { //Ignore all return values and execute only the following code fmt.Println("Successful execution") }
Detailed discussion of two ways of traversing strings:
If the string contains Chinese, the traditional way of traversing the string is error and garbled code will appear. The reason is that the traditional traversal of strings is based on bytes, while a Chinese character in utf-8 encoding corresponds to three bytes.
How to solve:
var str string = "hello,Beijing" str2 := []rune(str) //Is to convert str to [] run for i := 0; i < len(str2); i++ { fmt.Printf("%c ", str2[i]) }
-
range clause
The left side of the range keyword represents a pair of index value pairs, which return different results according to different expressions
The output of range return value is shown in the following table:
The type returned by the expression on the right First value Second value string index str[index], return type is run array/slice index str[index] map key m[key] channel element -
Implementing while with for
for { if Cyclic conditional expression{ break } Cyclic operation Cyclic variable iteration }
-
do... while with for
for { Cyclic operation Cyclic variable iteration if { break } }
Labels, jump statements
Tags can label for, switch, select and other process control code blocks. With tag identifiers, you can easily jump to a certain place to continue execution, which helps to improve programming efficiency. It is recommended to use capital letters and numbers for label names. Tags can mark any statement and are not limited to process control statements. Unused tags will cause errors
1. break statement
The break statement is used to terminate the execution of a statement block, interrupt the current for loop or jump out of the switch statement.
Precautions and details for use of break
-
When break appears in a multi-layer nested statement block, you can indicate which layer of statement block to terminate through a label
lable2: for i := 0; i < 4; i++ { //lable1: for j := 0; j < 10; j++ { if j == 2 { //break will jump out of the nearest for loop by default //break lable1 // The effect is the same as not writing lable1 break lable2 //Execute to this point and exit the two-tier for loop directly } fmt.Println("j=", j) } } break The default will jump out of the nearest for loop break You can specify the label later and jump out of the corresponding field of the label for loop
2. continue statement
The continue statement ends this cycle and continues to execute the next cycle
When the continue statement appears in the body of a multi-layer nested loop statement, you can use the label to indicate which layer of loop to skip. This is the same as the previous break label
lable2: for i := 0; i < 4; i++ { //lable1: for j := 0; j < 10; j++ { if j == 2 { continue //Output four times without j=2 each time, } fmt.Println("j=", j) } }
3. goto statement
goto statement can jump unconditionally to the labeled statement in the same function.
goto statements are usually used together with conditional statements to realize conditional transfer, jump out of the loop body, etc
goto is generally not advocated in go programming, so as not to cause confusion in the program flow and make it difficult to understand and debug the program
4,return
return when used in a method or function, it means to jump out of the method or function
- If return is in an ordinary function, it means that the function jumps out, that is, the code after return in the function will not be executed. It can also be understood as a termination function
- If return is in the main function, it means to terminate the main function, that is, to terminate the program
Deferred statement
defer is used to delay the calling of the specified function. defer can only appear inside the function.
Because of the delay characteristics of defer, it can be used to recover resources, clean up and close out. After using the defer statement, you don't have to worry about where the code is placed. Anyway, it is the last execution
In functions, programmers often need to create resources (such as database connection, file handle, lock, etc.). In order to release resources in time after the function is executed, the defer delay mechanism can be used
Notes and details of defer usage:
- The expression after defer must be a call to an external function
- Only when the defer statement is fully executed can the function in which the defer is located really end execution
- When there is a defer statement in the function, you need to wait until all defer statements are executed before executing the return statement
- When multiple defer statements appear inside a function, the last defer statement is executed first. (defer is actually a stack, which follows the last in, first out)
package main import "fmt" var i int = 0 func print(i int) { fmt.Printf("%d,", i) } func main() { //defer fmt.Printf("world") //fmt.Printf("hello") / / output: 5,5,5,5,5, for ; i < 5; i++ { defer print(i) } } //Output: 4,3,2,1,0,
-
When defer puts the statement on the stack, it will also copy the relevant values into the stack at the same time
func sum (n1 int, n2 int) int { defer fmt.Println("ok1 n1=",n1) //ok1 n1=10 defer fmt.Println("ok2 n2=",n2) //ok2 n2=20 n1++ //n1=11 n2++ //n2=21 res := n1 + n2 //32 fmt.Println("res=",res) return res } func main(){ res := sum(10,20) fmt.Println("res=",res) } Output result: ok3 res= 32 ok2 n2= 20 ok1 n1= 10 res= 32
-
Because of the delay characteristics of defer, it can be used to recover resources, clean up and close out. After using the defer statement, you don't have to worry about where the code is placed. Anyway, it is the last execution
- A common practice in golang programming is to add defer file after creating resources, such as opening files, obtaining links to databases, or locking resources Close() defer connect. Close()
- After defer, you can continue to use to create resources
- After the function is executed, the system will take out the statements from the defer stack in turn and close the resources
function
The set of instructions (statements) that complete a function is called a function
In go, functions are divided into user-defined functions and system functions
The introduction of functions into the program can reduce the redundancy of the code and facilitate the maintenance of the code
func Function name (formal parameter list) (return value list){ Execute statement... return Return value list } 1.Formal parameter list: represents the input of the function 2.Statement in function: it refers to the code block to realize a common function 3.A function can have a return value or no return value
Notes and instructions for using the function:
-
Go language functions do not support nesting, overloading, and default parameters
-
Function names can be called by other packages if they are capitalized, but not lowercase
-
go language supports returning multiple values, which is not available in other languages
func Function name (parameter list ) (Return value type list){ sentence.. return Return value list } 1.If multiple values are returned and you want to ignore a value when accepting, use_Symbol placeholder ignored 2.If there is only one return value, (return value list) can not be written ()
-
The variables in the function are local and do not take effect outside the function
-
The basic data type and array are passed by value by default, that is, the value is copied. Modification within the function will not affect the original value
-
If you want the variables inside the function to modify the variables outside the function, you can pass in the address &, and operate the variables in the function in the form of pointers
-
The go language does not support function overloading
-
In go, function is also a data type, which can be assigned to a variable, which is a variable of function type, through which the function can be called
func getSum(n1 int,n2 int) int { return n1 + n2 } func main(){ a := getSum //The type of a is func(int, int) int, and the type of getSum is func(int, int) int fmt.Printf("a The type of is%T,getSum The type of is%T\n",a,getSum) res := a(10,40) //Equivalent res: = getsum (10,40) fmt.Println("res=",res) }
-
Since the function is a data type, in go, the function can be used as a formal parameter and called
func getSum(n1 int,n2 int) int { return n1 + n2 } func myFun(funvar func(int, int) int, num1 int, num2 int) int { return funvar(num1, num2) } func main(){ res2 := myFun(getSum, 30, 30) fmt.Println("res2=", res2) }
-
To simplify data type definition, go supports custom data types (equivalent to an alias)
- Basic syntax: type custom data type name data type
-
Support naming function return value
func getSumAndSub(n1 int, n2 int) (sum int, sub int){ sub = n1 - n2 sum = n1 + n2 //You can no longer write the return value after return return } func main(){ a1, b1 := getSumAndSub(3,4) fmt.Printf("a1=%v,b1=%v",a1,b1) //a1=7,b1=-1 }
-
Use_ Identifier, ignoring return value
-
go supports variable parameters
- Args is slice slice, and each value can be accessed through args[index]
- If there are variable parameters in the formal parameter list of a function, the variable parameters need to be placed at the end of the formal parameter list
//0 to more than one parameter is supported func sum(args... int) int{ } //Support 1 to more parameters func sum (n1 int,args... int) int { }
func sum(n1 int, args ...int) int { sum := n1 for i := 0; i < len(args); i++ { sum += args[i] } return sum } func main(){ res4 := sum(10, 10, 10, 10, 10, 10) fmt.Println(res4) }
Built in function
For convenience, the golang setter provides some functions that can be used directly, which are called built-in functions (built-in functions)
-
len: used to find the length, such as string, array, slice, map and channel
-
new: used to allocate memory. It is mainly used to allocate value types, such as int, float32, struct... Returns pointers
func new(Type) *Type Built in function new Allocate memory. Its first argument is a type, not a value. Its return value is a pointer to the newly assigned zero value of the type num := new(int) fmt.Printf("num Type of%T,num Value of%v,num Address of%v,num The value pointed to is%v\n", num, num, &num,*num) //Type of num * int, value of num 0xc0000018030, address of num 0xc000006058, value pointed to by num is 0
-
make: used to allocate memory. It is mainly used to allocate reference types, such as chan, map and slice
func make(Type, size IntegerType) Type Built in function make Allocates and initializes an object of type slice, map, or channel. Its first argument is a type, not a value. make The return type of is the same as its parameter, not a pointer to it. The specific results depend on the specific type: section: size Its length is specified. The capacity of the slice is equal to its length. Slicing supports the second integer argument, which can be used to specify different capacities; It must not be less than its length, so make([]int, 0, 10) A slice with a length of 0 and a capacity of 10 is allocated. ( cap10 Yes (optional) Mapping: the creation of the initial assignment depends on size,But the resulting mapping length is 0. size It can be omitted, in which case one will be assigned Small starting size. Channel: the cache of the channel is initialized according to the specified cache capacity. if size If it is zero or omitted, the channel is uncached.
init function
Each source file can contain an init function, which will be called by the Go running framework before the main function is executed, that is, the init function will be called before the main function
-
If a file contains global variables, init function and main function at the same time
- The execution order is global variable definition - > init function - > main function
-
If main Go introduce utils Go, both contain variable definitions and init functions. What is the order of execution?
utils Global variable definition in -> utils in init function -> main Definition of global variables in -> main in init function -> main function
package main import "fmt" var age = test() //To see that the global variable definition is initialized first, write a test function here func test() int{ fmt.Println("test()") return 66 } //Init function, you can usually complete initialization in init function func init(){ fmt.Println("init()") } func main(){ fmt.Println("main()") } results of enforcement test() init() main()
Anonymous function
go supports anonymous functions. Anonymous functions are functions without names. If we want to call a function only once, we can consider using anonymous functions. Anonymous functions can also be called multiple times
- The anonymous function can be called directly when it is defined. In this way, the anonymous function can only be called once
func main(){ res := func(n1 int, n2 int) int { return n1 + n2 }(10,20) }
- Assign the anonymous function to a variable (function variable), and then call the anonymous function through the variable
a := func(n1 int, n2 int) int { return n1 + n2 } res1 := a(10,20) res2 := a(20,30)
- If you assign an anonymous function to a global variable, the anonymous function becomes a global anonymous function, which can be effective in the program
closure
A closure is a whole (entity) composed of a function and its related reference environment. In essence, a closure returns a function
A closure inherits the scope of the function declaration. This state (variables within the scope) will be shared in the closure environment, so these variables can be operated in the closure until they are destroyed.
Closures are often used as wrapper functions. One or more parameters are predefined for wrapping. Another common application is to use closures to complete more concise error checking
package main import "fmt" //accumulator func addUpper() func(int2 int) int { var n int = 10 var str = "hello" return func(x int) int { n = n + x str += "a" fmt.Println("str=", str) return n } } func main() { f := addUpper() fmt.Println(f(1)) fmt.Println(f(5)) fmt.Println(f(10)) fmt.Println(f(40)) } Output result: str= helloa 11 str= helloaa 16 str= helloaaa 26 str= helloaaaa 66 Description of the above functions 1.AddUpper()Is a function, and the data type returned is fun(int)int 2.Description of closure: var n int = 10 return func(x int) int { n = n + x return n } This is a closure. The returned function is an anonymous function, but the anonymous function is referenced outside the function n,So this anonymous function and n It forms a whole and constitutes a closure. 3.It can be understood that a closure is a class and a function is a method, n Is an attribute. A function and the variables it uses form a closure 4.Call us repeatedly AddUpper Function, n Initialize only once, 5.The key to find out the closure is to analyze the variables used by the returned function, because the function and the variables it references constitute the closure.
Practice on closures:
Write a makeSuffix function that can receive a file suffix (such as. jpg) and return a closure. Call closure to pass in a file name. If the file name has no specified suffix (such as. jpg), the file name will be returned jpg. If there is a suffix (such as. jpg), the original file name will be returned
Reference knowledge: func HasSuffix(s, suffix string) bool, which can judge whether a string has a specified suffix
func makeSuffix(suffix string) func(string) string { return func(name string) string { if !strings.HasSuffix(name, suffix) { //If name does not specify a suffix, it will be added; otherwise, the original name will be returned return name + suffix } return name } } func main() { f := makeSuffix(".jpg") fmt.Println("After document processing=", f("winter")) fmt.Println("After document processing=", f("summer.jpg")) } 1.Returned anonymous functions and makeSuffix(suffix string)of suffix Variables are combined into a closure, and the returned function is referenced to suffix This variable
Function value transfer mechanism
Value passing and reference passing
In fact, whether it is value transfer or reference transfer, what is passed to the function is a copy of the variable. Value passing is a copy of the value, and reference passing is a copy of the address. Generally speaking, address copying is efficient because of the small amount of data
- Value types: basic data types: int series, float series, bool, string, array and struct
- Reference types: pointer, slice slice, map, pipe chan, interface, etc
Common system functions in string
Function call: package name Function name (parameter)
-
Count the length of the string in bytes len(str). Built in function, no need to quote package
func len(v Type) int Built in function len return v Depending on the type: Array: v Number of elements in Array pointer:*v Number of elements in( v by nil Time panic) Slice, map: v The number of elements in the; if v by nil,len(v)Is zero character string: v Number of bytes in //ASCII characters occupy one byte and Chinese characters occupy three bytes Channel: the number of queued (unread) elements in the channel cache; if v by nil,len(v)Is zero
-
String traversal, while dealing with Chinese problems R: = [] run (STR)
Of course, the traversal of strings can use for range, which does not need to be sliced
str := "hello Beijing" fmt.Println("str len=",len(str)) //11 str2 := []rune(str) for i := 0; i <len(str2); i++ { fmt.Printf("character=%c ",str2[i]) }
-
String to integer: Atoi function in strconv package
func Atoi(s string) (i int, err error) Atoi yes ParseInt(s, 10, 0)Short for.
str := "hello" n,err := strconv.Atoi(str) if err != nil { fmt.Println("Conversion error",err) }else { fmt.Println("The result of the conversion is",n) }
-
Integer to string: Itoa function in strconv
func Itoa(i int) string Itoa yes FormatInt(i, 10) Short for.
-
String to [] byte: VAR bytes = [] byte ("hello,go")
var bytes = []byte("hello,go") fmt.Printf("bytes=%v\n",bytes) //Output: bytes = [104 101 108 111 44 103 111]
-
[] byte to string: str = string([]byte{97,98,99})
str = string([]byte{97,98,99})
-
Conversion from decimal to 2,8,16 process: STR = strconv FormatInt(133,2)
func FormatInt(i int64, base int) string return i of base Hexadecimal string representation. base It must be between 2 and 36, and lowercase letters will be used in the results'a'reach'z'Indicates a number greater than 10.
-
Find whether the substring is in the specified string:
func Contains(s, substr string) bool Judgment string s Whether to include substrings substr.
-
Count the number of specified substrings in a string
func Count(s, sep string) int Return string s There are several non repetitive sep Substring.
-
Case insensitive string comparison (= = case sensitive)
func EqualFold(s, t string) bool Judge two utf-8 Encoding string unicode Whether the characters in upper case, lower case and title are the same.
-
Returns the index value of the first occurrence of the string in the string. If not, - 1 is returned
func Index(s, sep string) int Substring sep In string s The position that appears for the first time in. If it does not exist, it returns-1.
-
Returns the index of the last occurrence of the substring. If not, - 1 is returned
func LastIndex(s, sep string) int Substring sep In string s The position of the last occurrence in the. If it does not exist, it will be returned-1.
-
Replace the specified substring with another substring: strings Replace ("go, go hello", "go", "go language", n) n can specify how many to replace. If n=-1, it means to replace all
func Replace(s, old, new string, n int) string Return will s Middle front n No overlap old Substrings are replaced with new New string, if n<0 Will replace all old Substring.
-
According to a specified character, a string is divided into a string array for segmentation identification: under the strings package
func Split(s, sep string) []string Remove with s Appears in sep The method of segmentation will be segmented to the end, and the slices composed of all the generated fragments (each one) will be returned sep Will be cut once, even two sep Adjacent, two cuts will also be made). If sep Null character, Split Will s Cut into each unicode The code value is a string.
strArr := strings.Split("hello,wrold,ok", ",") for i := 0; i < len(strArr); i++ { fmt.Printf("str[%v]=%v\n", i, strArr[i]) } fmt.Printf("strArr=%v\n", strArr) The output is: str[0]=hello str[1]=wrold str[2]=ok strArr=[hello wrold ok]
-
Converts the letters of a string to case
func ToLower(s string) string Returns a copy that converts all letters to the corresponding lowercase version. func ToUpper(s string) string Returns a copy that converts all letters to the corresponding uppercase version.
-
Remove the spaces on the left and right sides of the string
func TrimSpace(s string) string Return will s All blanks at the front and rear ends( unicode.IsSpace Specifies the string to remove.
-
Remove the characters specified on the left and right sides of the string
func Trim(s string, cutset string) string Return will s All front and rear ends cutset Contained utf-8 A string whose code values are removed.
-
Remove the specified character from the left of the string
func TrimLeft(s string, cutset string) string Return will s Front end all cutset Contained utf-8 A string whose code values are removed.
-
Remove the specified character from the right side of the string
func TrimRight(s string, cutset string) string Return will s Back end all cutset Contained utf-8 A string whose code values are removed.
-
Determines whether the string starts with the specified string
func HasPrefix(s, prefix string) bool judge s Is there a prefix string prefix.
-
Determines whether the string ends with the specified string
func HasSuffix(s, suffix string) bool judge s Is there a suffix string suffix.
Time and date correlation function
Date related functions are often used in programming, such as counting the time spent on the execution of a piece of code
-
Date and time related functions need to import the time package
-
time.Time type, used to represent time
now := time.Now() fmt.Printf("now=%v type=%T\n",now,now) //The output result is: now = 2022-04-02 21:20:23.5149432 + 0800 CST M = + 0.004880201 type = time Time fmt.Printf("year=%v\n", now.Year()) fmt.Printf("month=%v\n", now.Month()) fmt.Printf("month=%v\n", int(now.Month())) fmt.Printf("day=%v\n", now.Day()) fmt.Printf("Time=%v\n", now.Hour()) fmt.Printf("branch=%v\n", now.Minute()) fmt.Printf("second=%v\n", now.Second()) The output is: year=2022 month=April month=4 day=2 Time=21 branch=29 second=11
-
format date
- Method 1: use Printf or Sprintf
fmt.Printf("Current date %d-%d-%d %d:%d:%d\n",now.Year(),now.Month(),now.Day(),now.Hour(),now.Minute(),now.Second) dateStr := fmt.Sprintf("Current date %d-%d-%d %d:%d:%d\n",now.Year(),now.Month(),now.Day(),now.Hour(),now.Minute(),now.Second) fmt.Printf("dateStr=%v\n",dateStr)
- Method 2: use time Format() method completed
func (t Time) Format(layout string) string Format according to layout Return in the specified format t The formatted text representation of the point in time represented by. layout Defined reference time: Mon Jan 2 15:04:05 -0700 MST 2006 The formatted string representation is used as an example of the desired output. The same formatting rules will be used to format the time. Predefined ANSIC,UnixDate,RFC3339 And other formats describe the standard or convenient representation of reference time. For more definitions and formats of reference time, see the of this package ANSIC And other layout constants.
fmt.Printf(now.Format("2006-01-02 15:04:05")) fmt.Println() fmt.Printf(now.Format("2006-01-02")) fmt.Println() fmt.Printf(now.Format("15:04:05")) fmt.Println() Output: 2022-04-03 11:12:52 2022-04-03 11:12:52 explain; "2006/01/02 15:04:05" Each number of this string is fixed and must be written like this
-
Constant of time
In the program, it can be used to obtain the time in the specified time unit. For example, if you want to get 100 milliseconds: 100 * time Millisecond
type Duration int64 Duration Type represents the elapsed time between two time points, in nanoseconds. The longest period that can be expressed is about 290 years. const ( Nanosecond Duration = 1 //nanosecond Microsecond = 1000 * Nanosecond //subtle Millisecond = 1000 * Microsecond //millisecond Second = 1000 * Millisecond //second Minute = 60 * Second //minute Hour = 60 * Minute //hour ) //The base number between milliseconds, subtleties and nanoseconds is 1000
-
time.Sleep sleep
//Output 1 number every 1 second i := 0 for { i++ fmt.Println(i) time.Sleep(time.Second) if i == 10 { break } }
-
time Unix and Unix nano methods
func (Time) Unix func (t Time) Unix() int64 Unix take t Expressed as Unix Time, i.e. from time point January 1, 1970 UTC Time point t Elapsed time in seconds. func (Time) UnixNano func (t Time) UnixNano() int64 UnixNano take t Expressed as Unix Time, i.e. from time point January 1, 1970 UTC It's time t Elapsed time in nanoseconds. If it's in nanoseconds unix Time out int64 The range that can be represented, and the result is undefined. Note that this means Time Zero value call UnixNano Method, the result is undefined.
Date and time function: Statistics code execution time
func test() { str := "" for i := 0; i < 100000; i++ { str += "hello" + strconv.Itoa(i) } } func main() { start := time.Now().Unix() test() end := time.Now().Unix() fmt.Printf("implement test()It takes time:%v\n", end-start) }
Generate random number
1,func (*Rand) Intn
func (r *Rand) Intn(n int) int
Returns a pseudo-random int value with a value range of [0,n). If n < = 0, it will panic.
2,func Seed
func Seed(seed int64)
Use the given seed to initialize the default resource to a certain state; If seed is not called, the default resource behaves as if Seed(1) was called.
error handling
- By default, when a panic occurs, the program exits (crashes)
- If we want to catch and handle the error after the error occurs, so as to ensure that the program can continue to execute, and give the administrator a prompt (e-mail, SMS)
Basic description:
- Go pursues concise and elegant language, so go does not adopt try... catch... finally
- The processing methods referenced in go are defer, panic and recover
- The usage scenarios of these exceptions can be described as follows: a panic exception can be thrown in go, and then the exception can be caught in defer through recover, and then handled normally
func division() { defer func() { err := recover() if err != nil { fmt.Println(err) //Error messages can be sent here fmt.Println("Send error message to administrator") } }() a := 10 b := 0 res := a / b fmt.Println("res=", res) } func main() { division() fmt.Printf("Here is main function") }
Custom error
go language, you can also customize the error, using errors New and panic built-in functions
- errors.New("error description"), a value of error type will be returned, indicating an error
- The panic built-in function accepts a value of interface {} type as a parameter, accepts a variable of error type, outputs an error message, and exits the program
func readConf(name string) (err error) { if name == "config.ini" { //read return nil } else { //A custom error was returned return errors.New("Error reading file...") } } func test01() { err := readConf("confige.ini") if err != nil { //If there is an error reading the file, output the error and terminate the program panic(err) } fmt.Println("test01()Continue execution") } func main() { test01() fmt.Println("main()Function continues") } /*File error: panic: Error reading file goroutine 1 [running]: main.test01() D:/MyCode/goWorkspace/gostudy/src/func4_2/errortest.go:36 +0x49 main.main() D:/MyCode/goWorkspace/gostudy/src/func4_2/errortest.go:44 +0x19 */
package
A package is actually a collection of functions and data. Create different folders to store program files
In actual development, you need to call functions defined in other files in different files, such as: main Go, use utils Function in go file.
Go manages the file and project directory structure in the form of package. The go language does not allow importing packages without using them
Function of package:
- Identifiers that distinguish functions and variables with the same name
- When there are many program files, it can manage the project well
- Control the access scope of functions and variables, i.e. scope
Package structure:
Go language compiler has strict requirements for source directory. Each workspace must be composed of bin, pkg and src directories. The bin directory is mainly used to store executable files; pkg directory stores compiled library files, mainly * a) documents; src directory is mainly used to store the source files of go language.
Source file of package:
The header of the source file must be consistently declared with the statement of package < name >. go language package can be composed of multiple files, so the file name does not need to be consistent with the package name. It is recommended to use lowercase letters for package names, and try not to use reserved names (main, all, std). The executable file must contain package main and the entry function main.
go determines the access rights of an object (global variable, global constant, type, structure, field, function and method) through the case of the initial letter
Notes and details of the package
-
When packaging a file, the package corresponds to a folder. The package name of the file is usually the same as the folder name where the file is located, usually in lowercase letters
-
When a file needs to use other package functions or variables, the corresponding package needs to be introduced first
-
The package instruction is on the first line of the file, followed by the import instruction
-
When importing the package, the path starts from src of $GOPATH. Without src, the compiler will automatically import from src
-
In order to allow files in other packages to access functions in this package, the first letter of the function needs to be capitalized
-
When accessing other package functions and variables, the syntax is package name Function name
-
If the package name is long, Go supports aliasing the package. Note: after aliasing, the original package name cannot be used
-
You cannot have the same function name (or the same global variable) in the same package
package main import ( "fmt" util "go_code/chapter/fundemo/utils" )
-
If you want to compile into an executable file, you need to declare the package as main, that is, package main. This is a syntax specification. If you write a library, the package name can be customized
-
Sort and find
Sorting is divided into internal sorting and external sorting;
Eight internal sorting algorithms:
Insert sort: directly insert sort and insert sort
Swap sort: bubble sort, quick sort
Select sort: simple select sort and heap sort
Merge sort
Cardinality sort
Bubble sort:
func BubbleSort(arr *[5]int) { fmt.Println("Before sorting arr=", (*arr)) temp := 0 fmt.Printf("arr=%T,*arr=%T\n", arr, (*arr)) for i := 0; i < len(arr)-1; i++ { for j := 0; j < len(*arr)-1-i; j++ { if (*arr)[j] > (*arr)[j+1] { temp = (*arr)[j] (*arr)[j] = (*arr)[j+1] (*arr)[j+1] = temp } } } fmt.Println("After sorting arr=", (*arr)) } func main() { var arr = [5]int{5, 3, 87, 6, 66} BubbleSort(&arr) fmt.Println("After sorting arr=", arr) }
Sequential search: traversal
Binary search: the sequence to be searched is required to be ordered
object-oriented
Golang also supports object-oriented programming (OOP), but different from traditional object-oriented programming, it is not a pure object-oriented language. Therefore, it is more accurate for us to say that golang supports object-oriented language.
Golang does not have classes. The structure of Go language has the same status as the classes of other languages. It can be understood that golang implements OOP features based on struct
Golang object-oriented programming is very concise, removing the inheritance, method overloading, constructor, destructor, hidden this pointer and so on of the traditional OOP language
Golang still has the characteristics of object-oriented inheritance, encapsulation and polymorphism, but the implementation method is different from other OOP languages. For example, golang does not have the extends keyword, and inheritance is realized through anonymous fields
Golang is very elegant object-oriented. OOP itself is a part of the language type system. It is associated through the interface. It has low coupling and is very flexible. Interface oriented programming is a very important feature in golang
structural morphology
Relationship between structure and structure variable (instance / object)
All cat features are extracted -- > cat structure {1. Field / attribute (Name, Age, Color) 2. Behavior (method)} -- > variable (instance), variable (instance)
- Extract the characteristics of a class of things to form a new data type, that is, a structure
- Through this structure, we can create multiple variables (instances / objects)
- Things can be cats, people, Fish, or tools
Case:
type Cat struct { Name string Age int Color string Hobby string Scores [3]int } func main() { //Use struct structure var cat1 Cat cat1.Name = "Xiaobai" cat1.Age = 3 cat1.Color = "white" cat1.Hobby = "Eat fish" fmt.Println(cat1) }
Differences and relations between structure and structure variables
It can be seen from the above example
- Structures are user-defined data types that represent a class of things
- Structural variables (instances) are concrete and practical, representing a specific variable
- Members of structure variables have default values
- Structure variables are value types
Declaration of structure:
type Structure name struct{ field1 type field2 type }
Fields / attributes:
- From the concept or name: structure field = attribute = field
- A field is an integral part of a structure. It is generally a basic data type, an array, or a reference type
Field / attribute considerations
- The syntax of field declaration is the same as that of variable
- The type of field can be basic type, array and reference type
- After creating a structure variable, if no value is assigned to the field, it corresponds to a zero value (default value),
- bool is false, the value is 0, and the string is' '
- The default value of array type is related to the type of element
- The zero values of pointer, slice and map are nil, that is, no space has been allocated
- The fields of different structure variables are independent and do not affect each other. The change of one structure variable field does not affect the other. The structure is a value type
Create structure variables and access structure fields
-
Method 1: direct declaration
var person Person
-
Mode 2: {}
var person Person {}
-
Mode 3:&
var person *Person = new(Person)
-
Mode 4: {}
var person *Person = &Person{}
func main() { //Method 1: direct declaration var p1 Person p1.Name = "xioaming" p1.Age = 17 //Mode 2: {} p2 := Person{"marry", 20} fmt.Println(p2) //Mode 3:& var p3 *Person = new(Person) //Since p3 is a pointer, the standard assignment method is (*p3).Name = "smith" (*p3).Age = 18 fmt.Println(*p3) //(*p3).Name = "smith" can also be written P3 Name = "smith" //Reason: for the convenience of programmers, go designers will use p3 Name = "Smith" and add value operation (* p3) to p3 Name = "smith" var p4 *Person = new(Person) p4.Name = "john" p4.Age = 20 fmt.Println(*p4) //Mode 4: {} //The following methods can also be used for direct assignment var p5 *Person = &Person{} //p5 is a pointer, the same as above p5.Name = "tom" p5.Age = 16 }
Memory allocation mechanism of struct type
-
All fields of the structure are continuous in memory
type Point struct { x int y int } type Rect struct { leftUp, rightDown Point } type Rect1 struct { leftUp, rightDown *Point } func main() { //r1 has four int s that are contiguous in memory r1 := Rect{Point{1, 2}, Point{3, 4}} fmt.Printf("r1.leftUp.x address=%p r1.leftUp.y address=%p r1.rightDown.x address=%p r1.rightDown.y address=%p\n", &r1.leftUp.x, &r1.leftUp.y, &r1.rightDown.x, &r1.rightDown.y) //1.leftUp.x address = 0xc000000e1c0 R1 leftUp. Y address = 0xc000000e1c8 R1 rightDown. X address = 0xc000000e1d0 R1 rightDown. Y address = 0xc000000e1d8 //r2 has two * Point types. The addresses of these two * Point types are continuous, but the addresses they Point to are not necessarily continuous r2 := Rect1{&Point{10, 20}, &Point{30, 40}} fmt.Printf("r2.leftUp Self address=%p r2.rightDown Self address=%p\n", &r2.leftUp, &r2.rightDown) fmt.Printf("r2.leftUp Point to address=%p r2.rightDown Point to address=%p\n", r2.leftUp, r2.rightDown) }
-
Structure is a user-defined type. It needs exactly the same fields (name, number, type) when converting with other types
type A struct { Num int } type B struct { Num int } func main() { var a A var b B a = A(b) fmt.Println(a, b) }
-
The structure is redefined by type (equivalent to taking an alias). Golang believes that it is a new data type. The structure after type cannot be directly assigned to the original data type, but it can be forcibly transferred to each other
-
A tag can be written on each field of struct, which can be obtained through reflection mechanism. The common use scenario is serialization and deserialization
Monster serialization [json] – > the string is sent to the client for processing
-
package main import ( "encoding/json" "fmt" ) type Monster struct { Name string `json:"name"` Age int `json:"age"` Skill string `json:"skill"` } func main() { monster := Monster{"Ox demon king", 500, "Banana fan"} //Serialize the monster variable into a json format string //json. Reflection in Marshal function jsonStr, err := json.Marshal(monster) if err != nil { fmt.Println("json Processing error", err) } fmt.Println("jsonStr", string(jsonStr)) }
method
In Golang, methods work on specified data types (i.e., bind to data types). Therefore, custom types can have methods, not just struct s.
Go method is a function that acts on the receiver. The receiver is a certain type of variable, so in go language, method is a special type of function
Declaration of method:
func (recv receiver_type) methodName(parameter_list)(return_value_list){
......
}
func (data type bound by variable name) method name (formal parameter list) (return value list){
Operation
}
type A struct{ Num int } func (a A) test(){ fmt.Println(a.Num) } //explain 1.func (a A) test() {} express A The structure has a method called test 2.(a A) reflect test The method is and A Type bound
Case:
type Person struct { Name string } func (p Person) test() { fmt.Println(p.Name) } func main() { var p Person p.Name = "tom" p.test() }
- The test() method is bound to the Person type
- rest() can only be called through Person type variables, and cannot be called directly or with variables of other types
- P in func(p Person) test() {} indicates which Person variable is called. This p is its copy, which is very similar to the function parameter passing. Changing the structure variable member in the method has no effect on the structure variable in the main function, except for the transmission address
- The name p is specified by the programmer and is not fixed. For example, it can be modified to person. Try to write something meaningful
How to get started:
-
Add the speak method to the Person structure, and the output xxx is a good Person
func (p Person) speak() { fmt.Println(p.Name, "He is a good man") }
-
Add the jisuan method to the Person structure and calculate the result from 1 + 1000. It shows that the method and function are the same
func (p Person) jisuan() { res := 0 for i := 1; i <= 1000; i++ { res += i } fmt.Println(p.Name, "The calculation result is:", res) }
-
Add jisuan2 to the Person structure. This method can accept a number N and calculate from 1 to n
func (p Person) jisuan2(n int) { res := 0 for i := 1; i <= n; i++ { res += i } fmt.Println(p.Name, "The result of calculation 2 is:", res) }
-
Add getSum to the Person structure. This method can calculate the sum of two numbers and return the result
func (p Person) getSum(n1 int, n2 int) int { return n1 + n2 }
-
Method call
var p Person p.Name = "tom" p.test() p.speak() p.jisuan() p.jisuan2(100) fmt.Println("sum=", p.getSum(3, 3))
Method invocation and parameter passing mechanism
The mechanism of method calling and parameter passing is basically the same as that of a function. The difference is that when a method is called, the variable calling the method will also be passed to the method as an argument (if the variable is a value type, the value will be copied. If the variable is a reference type, the address will be copied)
Case:
type Circle struct { radius float64 } func (c Circle) area() float64 { return 3.14 * c.radius * c.radius } func main() { var circle Circle circle.radius = 1.0 fmt.Println("Ciecle area =", circle.area()) } //Note: radius in return 3.14 * c.radius * c.radius is copied func (c *Circle) area() float64 { return 3.14 * c.radius * c.radius } What's in this radius Is in the main function radius
Precautions and details of the method
-
The structure type is a value type. In the method call, it follows the value transfer mechanism and is the value copy transfer mode
-
If the programmer wants to modify the value of structure variable in the method, it can be handled by structure pointer
type Circle struct { radius float64 } func (c *Circle) area() float64 { fmt.Printf("c yes *Circle Address to=%p\n", c) c.radius = 2 //c.radius is equivalent to (* c) radius return 3.14 * c.radius * c.radius } func main() { var circle Circle circle.radius = 1.0 fmt.Printf("main circle Structure variable address =%p\n", &circle) fmt.Println("Ciecle area =", circle.area()) }
-
In Golang, methods work on specified data types (i.e., bind to data types). Therefore, custom types can have methods, not just struct s.
type integer int func (i integer) print() { fmt.Println("i=", i) } func main() { var i integer i.print() }
-
The access scope control of method is the same as that of function. The method name is lowercase and can only be accessed in this package. The method name is capitalized and can be accessed in this package and other packages
-
If a method implements the String() method, FMT Println will call String() of this variable by default for output
type Student struct { Name string Age int } //Implement String() for students func (stu *Student) String() string { str := fmt.Sprintf("Name=[%v] Age=[%v]", stu.Name, stu.Age) return str } func main() { stu := Student{"msk", 20} fmt.Println(&stu) }
Difference between method and function
-
The calling method is different
Functions: function name (argument list)
Method: variable Method name (argument list)
-
For ordinary functions, when the receiver is of value type, the data of pointer type cannot be passed directly, and vice versa
-
For methods (such as struct methods), when the receiver is a value type, you can directly call the method with a pointer type variable, and vice versa. Tip: see whether the value type or pointer type is bound in the method
- Regardless of the call form, the real decision is whether to copy the value or address, depending on which type the method is bound to
encapsulation
Encapsulation is to encapsulate the abstract fields and field operations. The data is protected internally. Other packages of the program can only operate the fields through authorized operations (Methods)
benefit
- Hide implementation details
- Verify the data to ensure safety and rationality
Packaging steps:
-
Lowercase the first letter of structure and field
-
Provide a factory mode function for the package where the structure is located, with the initial capital. Similar to a constructor
-
Provide an initial capitalization Set method to judge and assign values to attributes
func (var Structure type name) SetXxx(parameter list)(Return value list){ //Add data validation logic var.field = parameter }
-
Provide an uppercase Get method to Get the value of the property
func (var Structure type name) GetXxx(){ return var.age }
Case:
package model import "fmt" type account struct { accountNo string pwd string balance float64 } func NewAccount(accountNo string, pwd string, balance float64) *account { if len(accountNo) < 6 || len(accountNo) > 10 { fmt.Println("Wrong number of account numbers") return nil } if len(pwd) != 6 { fmt.Println("Wrong number of password digits") return nil } if balance < 20 { fmt.Println("The initial balance is less than 20!") return nil } return &account{ accountNo: accountNo, pwd: pwd, balance: balance, } } func (account *account) Depsodite(money float64, pwd string) { if pwd != account.pwd { fmt.Println("Password input error!") return } if money <= 0 { fmt.Println("Incorrect amount entered!") return } account.balance += money fmt.Println("Deposit successful") } func (account *account) WithDraw(money float64, pwd string) { if pwd != account.pwd { fmt.Println("Password input error!") return } if money > account.balance || money < 0 { fmt.Println("Insufficient account balance!") } else { account.balance -= money fmt.Println("Withdrawal succeeded") } } func (account *account) Query(pwd string) { if pwd != account.pwd { fmt.Println("Password input error!") } fmt.Println(account.accountNo, "Your account balance is:", account.balance) }
package main import ( "fmt" "gostudy/src/oop4_15/exer/factory/model" ) func main() { account := model.NewAccount("123456", "234564", 20) account.WithDraw(20, "234564") account.Query("234564") account.Depsodite(1000, "234564") account.Query("234564") }
inherit
Inheritance can improve code reusability and make our programming closer to human thinking. Solve code redundancy, facilitate function expansion and code maintenance
When the same attributes (fields) and methods exist in multiple structures, structures can be abstracted from these structures. (teenagers, teenagers, adults and the elderly all inherit Person). Therefore, after the structure Perosn is abstracted, the same attributes in other structures do not need to be defined repeatedly, and only one Person anonymous structure needs to be nested
Details:
-
Structures can use all fields and methods in nested anonymous structures, that is, fields and methods with uppercase or lowercase letters can be used
type A struct { Name string Age int sal float64 } func (a *A) sayOK() { fmt.Println("Hello", a.Name) } func (a *A) saySal() { fmt.Println(a.Name, "The salary is:", a.sal) } type B struct { A } func (b *B) sayOk(){ fmt.Println("Hello",b.Name) } func main() { var b B b.A.Name = "Tom" b.A.Age = 21 b.A.sal = 33333 b.A.saySal() }
-
Anonymous structure field access can be simplified
func main() { //var b B //b.A.Name = "Tom" //b.A.Age = 21 //b.A.sal = 33333 //b.A.saySal() var b B b.Name = "Tom" b.Age = 21 b.sal = 33333 b.saySal() } Summary of the above codes When we go straight through b When accessing fields or methods, such as b.Name,Its execution process: the compiler will look at it first b Is there a corresponding type Name,If so, call directly B Type Name Field. If not, go see it B Embedded anonymous structure A Is there a statement in the Name Field, call if there is one, continue to search if there is none, and report an error if there is none
-
When the structure and anonymous structure have the same fields or methods, the compiler adopts the principle of nearest access. If you want to access the fields and methods of anonymous structure, you can distinguish them by the name of anonymous structure
func main() { var b B b.Name = "Tom" //Proximity principle b.A.Name = "jack" //The Name field in A anonymous structure is explicitly accessed b.Age = 18 b.sayOK() //Proximity principle b.A.sayOK() //The explicit access is to the sayOk() method in the A anonymous structure }
-
Two (or more) anonymous structures are embedded in the structure. If two anonymous structures have the same fields and methods (and the structure itself does not have the same fields and methods), the name of the anonymous structure must be clearly specified during access, otherwise the compilation will report an error
-
If a struct is nested with a well-known structure, this pattern is a combination. If it is a combination relationship, the name of the structure must be brought when accessing the fields or methods of the combined structure
type D struct { a A //Famous structure } func main(){ //For example, if there is a famous structure in D, the name of the famous structure must be brought when accessing the fields of the famous structure var d D d.a.Name = "jack" }
-
After nesting anonymous structures, you can also directly specify the values of each anonymous structure field when creating a structure variable (instance).
type TV1 struct { Goods Brand } type TV2 struct { *Goods *Brand } func main() { tv1 := TV1{Goods{"TV 01", 1200}, Brand{"Haier", "Shandong"}} tv2 := TV1{ Goods{ "TV 02", 1100, }, Brand{ "SHARP", "Beijing", }, } fmt.Println(tv1) fmt.Println(tv2) tv3 := TV2{&Goods{"TV 03", 1000}, &Brand{"SKYWORTH", "Shanghai"}} tv4 := TV2{ &Goods{ "TV 04", 999, }, &Brand{ "millet", "Shanghai", }, } fmt.Println(*tv3.Goods, *tv3.Brand) fmt.Println(*tv4.Goods, *tv4.Brand) }
-
If there is an anonymous field of type int in a structure, there cannot be a second one. If you need more than one int field, you must specify a name for the int field
multiple inheritance
If a struct is nested with multiple anonymous structures, the structure can directly access the fields and methods of the nested anonymous structures, so as to realize multiple inheritance
type Goods struct { Name string Price float64 } type Brand struct { Name string address string } type TV1 struct { Goods Brand }
Details of multiple inheritance:
- If the embedded anonymous structure has the same field name or method name, it needs to be distinguished by the anonymous structure type during access
- In order to ensure the simplicity of the code, it is recommended not to use multiple inheritance as much as possible
Interface
The interface type can define a set of methods, but these do not need to be implemented. And the interface cannot contain any variables. When a user-defined type (such as structure Phone) is to be used, these methods will be written out (implemented) according to the specific situation
Basic syntax:
type Interface name interface{ method1(parameter list) Return value list method2(parameter list) Return value list } func (t Custom type) method1(parameter list) Return value list{ //Method implementation } func (t Custom type) method2(parameter list) Return value list{ //Method implementation }
Description:
-
All methods in the interface have no method body, that is, the methods of the interface have no implementation. The interface embodies the polymorphism, high cohesion and low coupling of programming
-
The interface in Golang does not need to be displayed. As long as a variable contains all the methods in the interface type, the variable implements the interface. Therefore, there is no keyword such as implement in Golang
-
The interface itself cannot create an instance, but it can point to a variable (instance) of a custom type that implements the interface
type AInterface interface { Say() } type Stu struct { Name string } func (stu Stu) Say() { fmt.Println("stu Say()") } func main() { var stu Stu var a AInterface = stu a.Say() }
-
In Golang, a user-defined type needs to implement all methods of an interface. We say that this user-defined type implements the interface
-
Only when a user-defined type implements an interface can an instance (variable) of the user-defined type be assigned to the interface type
-
As long as it is a user-defined data type, you can implement an interface, not just a structure type
type AInterface interface { Say() } type Integer int func (i Integer) Say() { fmt.Println("Integer Say i =", i) } func main() { var i Integer = 10 var b AInterface = i b.Say() }
-
A custom type can implement multiple interfaces
-
Cannot have variables in Golang interface
-
For example, interface A and interface B can also implement one or more interfaces (for example, if interface A and interface b) respectively
-
The interface type is a pointer (reference type) by default. If it is used without interface initialization, nil will be output
-
The empty interface {} has no method. All types implement the empty interface, that is, we can assign any variable to the empty interface
func main() { var i Integer = 10 var b AInterface = i b.Say() var t I = i var t2 interface{} = i var num1 float64 = 8.9 t2 = num1 t = num1 fmt.Println(t, t2) }
Factory mode
There is no constructor (constructor) in the structure of Golang, which can usually be solved by using the factory pattern
package model type Student struct{ Name string ...... } When Student Capitalize if you want to create in another package Student Examples, introduction model Just a bag, but when student If the initial is lowercase, it won't work --> Factory mode solution
Case:
When the Student structure is capitalized, there is no problem
package model type Student struct { Name string Score float64 }
package main import ( "fmt" "gostudy/src/oop4_15/exer/factory/model" ) func main() { var stu = model.Student{"msk", 99} fmt.Println(stu) }
When student initial is lowercase, factory mode:
package model type student struct { Name string Score float64 } //Because the initial letter of student structure is lowercase, it can only be used in model //We solve it through factory mode func NewStudnet(n string, s float64) *student { return &student{ Name: n, Score: s, } } //If the field is lowercase, it cannot be accessed directly in other packages Write a return method, call stu. middle note in other packages GetScore() func (s *student) GetScore() float64{ return s.score }
package main import ( "fmt" "gostudy/src/oop4_15/exer/factory/model" ) func main() { var stu = model.NewStudnet("tom", 88) fmt.Println(stu) fmt.Println("name=", stu.Name, "score=", stu.Score) }