golang study notes
- Opening: first study, understanding is still shallow. For the time being, take some notes and enrich them slowly in the future, and then correct or add them~
Chapter I: Enlightenment and environmental installation
-
Installation link (non official website): https://studygolang.com/dl
-
Terminal input: go env to view the image resource
-
Change a software download source:
- windows: go env -w GOPROXY=https://goproxy.cn,direct
- linux: export GOPROXY=https://goproxy.cn
-
Set GO111MODULE to on: go env -w GO111MODULE=on
- GOPATH: the previous package management tool will be mentioned later
- GOMODULE: current package management tool
-
Installation tool: go get -g -v golang.org/x/tools/cmd/goimports
-
Installing go language editor: GoLand
- https://www.jetbrains.com/go/download/other.html
- Download version 2020.1.1
- Video of station B: Goland installation and configuration
-
Click settings to configure the relevant environment.
- GOROOT: click and the go you installed will automatically appear. Select it. (note! GoLand should not recognize Chinese. It was installed on Disk C by default. I guess this is the reason. I can't find the go installation location. Then install a go in the pure English path of disk d to display ~)
- GPPATH: as will be mentioned later, there are requirements for directory structure. (configure it casually. For example, create a folder called gopath and another folder called src)
- GoModules: this configuration is the mainstream GO111MODULE. At present, go depends on management. Select the corresponding Vgo, and then fill in Environment: https://goproxy.cn,direct changes the download source.
-
Create project:
-
-
Set proxy: settings - > go - > Go Modules (VGO), check Enable Go Modules (vgo) integration to enable Go Modules, and enter in the environment input box:
https://goproxy.cn,direct
-
-
- Set settings - > go - > gopath, and check index entity gopath to index the entire gopath. You can also create a gopath for the project alone. (if you use go Modules, you don't need to configure this. I think ~)
-
- Go, Go Modules, Dep, App Engine: select GO Modules
- Location: set the project directory.
- Go, Go Modules, Dep, App Engine: select GO Modules
-
-
a minor question
// fmt.Println(a...: 123) this a... Is actually the name of the println parameter // It's shown by goland. You can turn it off in settings // 1. Search for parameter hint in the setting. In 2020.1.1, Go under Inlay Hints. Click Show parameter hint and apply it.
-
To use vscode, you need to install a bunch of plug-ins (automatic prompt for installation)
- Command line go run + filename execution
- go mod init + module name to generate mod file
- (in addition) go build compilation file
Chapter two: basic grammar
1. Method of defining variables:
package main // Variables can also be defined outside the function, but only var can be used // In addition, this is a variable within the package (the scope is within the package), and there is no global variable var aa = 33 // You can also write this way var ( bb int = 1 cc string = "123" dd float32 = 2.01 ) func variable() { var a, d int = 1, 2 // Var a, d = 1, 2 can also be assigned according to the type assigned later. There is no need to declare it. The compiler can infer the variable type // var a, b, c , s = 3, 4, true, "def" // P: = 10 can also replace the var keyword var b int var s string = "123" fmt.Println(a, b, s) fmt.Printf("%d %q\n", a, s) // Print in format, and the defined variables must be used! fmt.Println(aa) }
2. Built in variable type
- bool, string
- (u)int, (u)int8, (u)int16, (u)int32, (u)int64, uinputer
- Byte, run (character type, for Multi Country applications, 32 bits in length, 4 bytes, unicode)
- float32, float64, complex64, complex128 (imaginary number, real part, imaginary part, 128 bits each)
- Type is cast
- var a, b = int = 3, 4
- var c int = math.Sqrt(a * a + b * b) error
- var c int = int(math.Sqrt(float64(a*a + b * b))) correct
- In addition, the float type is inaccurate, every language exists, and binary cannot accurately represent decimal
3. Constant definition
func consts() { const filename string = "123.txt" // Displays the indicated type const a, b float32 = 3, 4 const b float32 = 4 // float64 must be added here var c int = int(math.Sqrt(float64(a*a + b*b))) fmt.Println(a, b, filename, c) } func consts() { const filename string = "123.txt" const a, b = 3, 4 // In this way, if a and b are text, they will be used as float s~ var c int = int(math.Sqrt(a*a + b*b)) fmt.Println(a, b, filename, c) } // Enumeration type func enums() { const( cpp = 0 // app = iota means self increment. Complex values are not required for the following_ You can skip a value. You say java doesn't want it. Its position is written as_ java = 1 python = 2 golang = 3 ) // Using iota to realize complex const ( b = 1 << (10 * iota) kb mb gb pb ) // The results are calculated according to the formula 1 1024 1048576 1073741824 1099511627776 }
4. Conditional statements
-
if else statement
if condition{ ... } else if { ... } else { ... } // Conditional assignment is supported, and the scope of the variable is in the if else statement if a, b := 2, 3; a == 1{ fmt.Println(a) } else { fmt.Println(b) fmt.Println("cannot print", nil) }
-
switch statement
// The switch statement does not need to add a break after every sentence, and there is a break after every case, unless fallthrough is used var result int var op = 1 switch op { case 1: result = 1 // Program execution is interrupted and an error panic is reported panic("error") case 2: result = 2 case 3: result = 3 default: result = 4 }
-
for statement
sum := 0 for a := 1; i <= 100; i++ { sum += i }
// Complex read files and print them line by line func forT(filename string) { file, err := os.Open(filename) // Judge whether it is abnormal if err != nil { panic(err) } scanner := bufio.NewScanner(file) // There is only this end condition. Omit directly; (neither the start condition nor the increment condition) is used as a while for scanner.Scan() { fmt.Println(scanner.Text()) }
// The increment condition can also be omitted. It is equivalent to an endless loop for { fmt.Println("abc") }
5. Function
// 1. Return single value func eval(a, b int, op string) int {} // 2. Return two int values func div(a, b int, op string) (int, int) { return a / b, a % b } // 3. You can also name the return value: func div(a, b int, op string) (q, r int) { return a / b, a % b } // When the function is executed, it can be generated automatically q, r := div(13, 3) // 4. When defined, it will return directly and automatically func div(a, b int, op string) (q, r int) { q := a / b r := a % b return // Automatically return q, r: This is not recommended. When the code is long, the readability is too poor } // 5. according to the above example, call in other functions. // This is an error because these are the two returned values. return div(a, b)[0] means to return the first value like python return div(a, b) // This is also wrong! ha-ha.. Two variables defined in go language must be used to avoid compilation errors q, r := div(a, b) return q // Correct writing_ Indicates that this value is not required q, _ := div(a, b) return q // 6. Generally speaking, multiple return values are not used indiscriminately. The second return value is generally the returned error, such as file, err: = OS. Open (filename) func div(a, b int, op string) (int, error) { if a == 1 { return 1, nil } else { return 0, fmt.Errorf("err....") // When an error occurs, an exception error is returned } } aaa, err := div(2, 3, "123") fmt.Println(aaa, err) // The results will be printed out // 0 err....123 // 7. Function as formal parameter // op is a function with two int type parameters, and the return value is int type func apply(op func(int, int) int, a, b int) { return op(a, b) // You can also get the original function name p := reflect.ValueOf(op).Pointer() // Gets the real pointer to the function opName := runtime.FuncForPc(p).Name() // Get function name } // 8. Anonymous function // apply is a function. apply (func (int, int) int, a, B, int) is called below. The intermediate function parameter transfer is an anonymous function, which is defined internally apply( func(a int, b int) int { return a }, 3, 4) ) // Function parameters: there are no default parameters, optional parameters, function overloading and other complex function parameter types in go language // There is a list of variable parameters func sum(numbers ...int) int{ // numbers can accept multiple int values s := 0 for i := range numbers { s += numsbers[i] } return s } // implement sum(1, 2, 3, 4, 5)
6. Pointer
// In C language int a = 100; // In the first line of code, * is used to indicate that p is a pointer variable, and in the second line of code, * is used to obtain the data pointed to by the pointer. int *p_a = &a; *p_a = 200; // go in var a int = 2 var pa *int =&a *pa = 3 // The pointer in go language is simple: it can no longer be operated. In C language, the header pointer of the list can be added continuously.
-
Parameter transfer in function: there is only one transfer method of value transfer in go language!
-
In go language, value transfer and pointer transfer can also be carried out. Equivalent to the function of reference.
// Pointers work better than references. Generally speaking, the basic built-in types of java and Python are value passing, for example: // In Python, a = 5 is passed into the function and will not affect the value of the variable. This is value passing. // list, container objects and custom type objects are all passed by reference // In the go language, pointer passing. var a int func f(pa *int){} // Passed &a in, the variable can be modified in the function even if it is of type int Take a simple example: swap two numbers var a, b int = 3, 4 // Value transfer cannot be exchanged, but the values of a and b are copied. The formal parameters are also called a and b func swap(a, b int) { a, b = b, a } swap(a, b) // Pointer passing: can be exchanged correctly func swap(a, b *int) { *a, *b = *b, *a } swap(&a, &b) // When an object is passed, the value is also passed. var cache Cache func f(cache Cache){} // Cache is an object of type cache. It does not store real data in memory, but pointers to data in memory, // Therefore, there is no problem with value passing. It is equivalent to copying the pointer of the object and operating on the same object
-
So when you see an object, should you pass it by value type or pointer type? In object encapsulation.
-
Chapter 3: built in container
(1) Array
1. Array
func array() { // One bit array definition var arr1 [5]int arr2 := [3]int {1, 2, 3} arr3 := [...]int{2, 4, 6, 8 ,10} // Two dimensional array: 4 rows and 5 columns var grid [4][5]int fmt.Println(arr1) fmt.Println(arr2) fmt.Println(arr3) fmt.Println(grid) // 1. Traversal for i:= 0; i < len(arr3); i++{ fmt.Print(arr3[i], " ") } // 2. Traversal: range fmt.Print("\n") for i := range arr3 { fmt.Println(arr3[i], " ") } // Equivalent to enumerate() in python, traversing indexes and values // If you only want the value, you can for, V: = range arr3, underlined instead for i, v := range arr3 { fmt.Println(i, v) } // Why use range? 1. Simple and beautiful. two }
-
Array is a value type!
// arr [5]int passes a value type like this. That is, the passed in array must be 5 in length. func printArray(arr [5]int) { arr[0] = 100 for i, v := range arr { fmt.Println(i, v) } } printArray(arr1) // yes printArray(arr2) // error // Because it is value transfer, it is equivalent to making a copy of the five elements of the array as a whole, and then changing the array in the function will not affect the outside! // If you want to change the original array in other languages, you need to pass a pointer func printArray(arr *[5]int) {} // In the array, the value of the external array can be modified by arr[0] = 100 printArray(&arr1)
-
In the go language, arrays are not used directly: instead, slices are used~
2. slice
arr := []int {1, 2, 3, 4 ,5 ,6 ,7 ,8} // Pay attention to the difference between arrays and arrays. Don't add // 1. The slice here is the same as python, s = [2, 3, 4, 5], closed on the left and open on the right s := arr[2:6] // 2. Other writing methods: arr[:6], arr[2:], arr [:] are the same as python // 3. slice is not a value type. It has a data structure inside. slice is a view of arr to see the contents specified in the array // Pass the slice of the array to the function, and modify it to change the external array func updateSlice(s []int) { s[0] = 100 } updateSlice(s) fmt.Println(arr) // It will become 100 // 4. If you want to operate on the original array, you can use slice slice updateSlice(arr[:]) // 5. Slices can be sliced. The result is a slice based on the current slice arr := [...]int {1, 2, 3, 4 ,5 ,6 ,7 ,8} s1 := arr[2:6] // s = [3, 4, 5, 6] s2 := s1[3:5] // s = [6 ,7] s3 := s1[3:] // s = [6] // You can see that you are over fetching, because the bottom layer is the view of arr. When s2 specifies [3:5] exceeds s1, the hidden part will be fetched // s3 takes s1[3:]. By default, you can only see the part of s1 displayed, that is, only one 6 is taken // 2D slice var a [][]string // perhaps b = make([][]string, 0) // assignment a = [][]string{[]string{"abc", "efg"}, []string{"abc", "mln"}} fmt.Println(a) // result [[abc efg] [abc mln]]
3. Underlying principle of slicing
slice param: ptr Points to the beginning element of the slice param: len Current slice length, when s[6] If the length is greater than this, an error will be reported. parma: cap(capacity), Pointer pair ptr How long is it to the end. // Note: // 1. slice can be extended backward, but cannot be extended forward. When the first element of the slice is greater than len, an error will be reported. s2 = s1[6:7] will report an error and will not be extended // 2. You can print out and obtain the values of len and cap fmt.Printf("s1= %v, len(s1)=%d, cap(s1)= %d\n", s1, len(s1), cap(s1)) // Results can be obtained s1= [3 4 5 6], len(s1)=4, cap(s1)= 6
[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-X79rhUOh-1635157982890)(.\slice.png)]
4. Add elements to slice
arr := [...]int {1, 2, 3, 4 ,5 ,6 ,7 ,8} s1 := arr[2:6] fmt.Printf("s1= %v, len(s1)=%d, cap(s1)= %d\n", s1, len(s1), cap(s1)) s2 := append(s1, 10) s3 := append(s2, 11) s4 := append(s2, 12) s5 := append(s4, 13) fmt.Println(s1) fmt.Println(s2) fmt.Println(s3) fmt.Println(s4) fmt.Println(s5) fmt.Println(arr) // The results are as follows: /* s1= [3 4 5 6], len(s1)=4, cap(s1)= 6 [3 4 5 6] [3 4 5 6 10] [3 4 5 6 10 11] [3 4 5 6 10 11 12] [3 4 5 6 10 11 12 13] [1 2 3 4 5 6 10 11] It can be seen that the maximum result of s1 is cap = 6, so in the later operation of slicing, 10 and 11 cover 7 and 8 in arr. Inserting 12 and 13 in the back will not affect the arr. Summary: when adding elements, if the cap is exceeded, the system will reallocate a larger underlying array and copy the original elements (refer to the underlying structure above) In addition: due to value passing, you must receive the return value of append. Because when append, the len, cap, etc. in the underlying array may change, and new returns need to be received again */
5. Other operations of slice: create, delete, and copy
- Create slice
// 1. Declare the variable type as slice var s []int // The default value is nil, and the variable definition in go is not initialized // 2. The second method: create an array, and there is a slice that declares that s1 is an array s1 := []int {2, 4, 6, 8} // 3. Create a slice with a fixed length s2 := make([]int, 16, 32) // len = 16, cap = 32, not value when cap, the default is len length // 4. You can append for i := 0; i < 100; i++ { fmt.Printf("len(s)=%d, cap(s)= %d\n", len(s), cap(s)) s = append(s, i * 2 + 1) } // Each expansion of the underlying array is multiplied by 2. The printout can be seen len(s)=0, cap(s)= 0 len(s)=1, cap(s)= 1 len(s)=2, cap(s)= 2 len(s)=3, cap(s)= 4 len(s)=4, cap(s)= 4 len(s)=5, cap(s)= 8 len(s)=6, cap(s)= 8 len(s)=7, cap(s)= 8 len(s)=8, cap(s)= 8 len(s)=9, cap(s)= 16 len(s)=10, cap(s)= 16 len(s)=11, cap(s)= 16 len(s)=12, cap(s)= 16 len(s)=13, cap(s)= 16 len(s)=14, cap(s)= 16 len(s)=15, cap(s)= 16 len(s)=16, cap(s)= 16 len(s)=17, cap(s)= 32 len(s)=18, cap(s)= 32 len(s)=19, cap(s)= 32 //5. copy: copy s1 to s2 copy(s2, s1) // 6. delete // Delete element with index value 3: s2[:3] + s2[4:] // The second value of append is a variable length parameter list, that is, multiple values can be added at one time. When adding a slice, add... After it // In addition, deleting an element does not affect the cap. The size remains the same, len - 1 s2 = append(s2[:3], s2[4:]...) // 7. Delete the element of the header front := s2[0] s2 := [1:] // 8. Delete tail element tail := s2[len(s2 - 1)] s2 := s2[:len(s2) - 1]
(2) Map
###1. Basic content
// 1. Create a map // Method 1 m := map[string] string { "name": "xiaoming", "course": "golang", "age": "18", } // Method 2. nil (equivalent to none in Python) in go language can participate in operation, which is equivalent to the use of empty map m1 := make(map[string]int) // m1 == empty map empty map var m2 map[string] int // m2 == nil empty // 2. When traversing the map, you can also use underscores_ Replace what you don't want // Note: the order of each traversal is not necessarily the same because of the structure of the map for k, v := range m { fmt.Println(k, v) } // 3. Get value courseName := m["course"] // When you get a non-existent value, you get an empty string, which corresponds to the null value of the data type. go language variables can be used without initialization courseName, exist := m["course"] // The second variable exist can judge whether the current value exists and return the bool value // standard notation if courseName, exist := m["course"]; exist { fmt.Println(courseName) } else { fmt.Println("error") } // 4. Delete element delete(m, "name")
2. Data type of map key value pair
- The map uses a hash table and must be comparable
- Except slice, map and function, all built-in types can be key
- The Struct type does not contain the above fields and can also be used as a key. (checked during compilation)
3. Real exercise
- Force buckle: 3. Longest substring without repeated characters
- My own solution, of course, is not as good as the official solution~
import "fmt" func lengthOfLongestSubstring(s string) int { charMap := map[byte] int {} i := 0 rst := 0 byteS := []byte(s) // Convert to byte slice for j := 0; j < len(byteS); j++ { if _, ok := charMap[byteS[j]]; !ok { charMap[byteS[j]] = 1 } else { // The current element appears repeatedly. Calculate the current window size first if rst < j - i { rst = j - i } // Remove duplication for _, ok := charMap[byteS[j]]; ok ; _, ok = charMap[byteS[j]]{ delete(charMap, byteS[i]) i += 1 } // Add current element to window charMap[byteS[j]] = 1 } } // Finally, there will be a position without judgment if rst < len(byteS) - i{ return len(byteS) - i } else { return rst } } // Ha ha, it's true. The brain hole is wide open. Remove the repetition. It should be a while loop. If you write with for, you should understand it; What are the three parts? Let me write them right // If s[j] is in the map, it will loop all the time, i++ for charMap[s[j]]; ok ; _, ok = charMap[s[j]]{} // You can also do this directly: by default, 0 is returned if the element is not found for charMap[s[j]] != 0 {} // Note: // 1. I and j variables are defined at the beginning to be used in the whole code!, for can only be used in the corresponding {} code block // 2. Charmap: = map [byte] int {} pay attention to this definition. go language handles characters // In addition, it should be noted that
###(3) Character and character processing
1. Brief description
- According to the previous real exercise, we can find that the go language represents characters.
- The article explains string, run and byte
func strings() { s := "ABC I'm a beginner!" // UTF-8 encoding, rune type fmt.Println(s, len(s)) // Print length is 19! // 1. Convert to byte type output for _, b := range []byte(s) { fmt.Printf("%X ", b ) } // The one byte representation is printed as follows: 3 bytes for Chinese // For example, E6 88 91 means "I", which is UTF-8 coding // 41 42 43 E6 88 91 E6 98 AF E5 88 9D E5 AD A6 E8 80 85 21 fmt.Println() // 2. Direct output for i, ch := range s { fmt.Printf("(%d, %X)", i, ch) } // Results: (0, 41)(1, 42)(2, 43)(3, 6211)(6, 662F)(9, 521D)(12, 5B66)(15, 8005)(18, 21) // ch is a four byte integer of type rune (equivalent to int32). // 6211 means "I", which is unicode coding. And you can see that the subscript is incremented by three. // "I": process, decode the s(string) character utf-8: E6 88 91, convert it to unicode 6211, and then save it in the rune type four byte type fmt.Println() // 3. Call utf8 this library fmt.Println(utf8.RuneCountInString(s)) // The print result is 9 // Get byte array bytes := []byte(s) // Return each character and character size, English 1, Chinese 3. decode for len(bytes) > 0 { ch, size := utf8.DecodeRune(bytes) bytes = bytes[size:] fmt.Printf("%c ", ch) } // Results A B C I'm a beginner! fmt.Println() // Convert to rune type for i, ch := range []rune(s) { fmt.Printf("(%d, %c)", i, ch) } // Results (0, A)(1, B)(2, C)(3, I) (4, yes) (5, beginning) (6, learning) (7, person) (8,!) }
- Summary:
- Use range to traverse pos and run pair
- Use utf8.RuneCountInString() to get the number of characters
- Get byte length using len
- Get bytes using [] byte
- Let's sort out the above content
package main import ( "fmt" "unicode/utf8" ) func main() { s := "ABC I just started learning go!" var runeS []rune = []rune(s) // Print 11, each rune is int32, 4 bytes fmt.Println(len(runeS)) var byteS []byte = []byte(s) // Print 21, the Chinese character set occupies three bytes fmt.Println(len(byteS)) // Return each character and character size, English 1, Chinese 3. decode for len(byteS) > 0 { ch, size := utf8.DecodeRune(byteS) byteS = byteS[size:] fmt.Printf("%c ", ch) } // Results A B C I'm a beginner! // 3. Call utf8 to get the desired length 11 fmt.Println(utf8.RuneCountInString(s)) } // Note: A: = [] byte (s) or a: = [] run (s) or s (strings are of run type by default) // They use a [2] and s [2] to get numbers, the special representation of strings in go
2. True title: International Version (support Chinese)
// The byte slice is represented as the character slice (run) func lengthOfLongestSubstring(s string) int { charMap := map[rune] int {} i := 0 rst := 0 runeS := []rune(s) for j, ch := range runeS { // Here ch is also a number, or directly for J: = 0; j < len(runsS); j++ if _, ok := charMap[ch]; !ok { charMap[ch] = 1 } else { // The current element appears repeatedly. Calculate the current window size first if rst < j - i { rst = j - i } // Remove duplication for charMap[ch] != 0{ delete(charMap, runeS[i]) i += 1 } // Add current element to window charMap[ch] = 1 } } // Finally, there will be a position without judgment if rst < len(runeS) - i{ return len(runeS) - i } else { return rst } }
- Finally, let's look at the comparison results:
[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-lkl44vpr-1635157982892) (. / run_byte_range. PNG)]
- Note that it is only used as a performance reference, which is not accurate. I tried it several times, but the time is not bad.
3. Other string operations
// There are many operations under the strings library The teacher didn't speak in detail, so I'll record it later
- summary
- Strings are separated or combined: Fields, Split, Join
- Contains, index: find substrings
- ToLower, ToUpper:
- Trim, TrimRight, TrimLeft
Chapter 4: structure and method "object oriented"
###1. Basic content
- Object oriented characteristics of go language
- Only encapsulation is supported, inheritance and polymorphism are not supported
- go language has no class, only struct
- definition
// Defines the node type of a tree type TreeNode struct { Left, Right *TreeNode // Pointer type Value int }
- establish
func main() { // 1. Definitions var root1 TreeNode root2 := TreeNode{} // initialization root1 = TreeNode{val: 3} root1.left = &root2 // Pay attention to the pointer and pass the address // Note that in go language, both instances and attributes are. For example, c + + may be - > root1.right = &TreeNode{val: 4} fmt.Println(root1.val) // 2. You can also define a slice nodes := []TreeNode { // Omit initialization data {val: 3}, {}, {nil, &root1, 6}, } fmt.Println(nodes) //Results: [{< nil > < nil > 3} {< nil > < nil > 0} {< nil > 0xc000004078 6}] }
###2. Constructor?
-
A structure has no constructor.
- Because there are many initialization methods described above. But sometimes we need to control its structure
- You can use factory functions, which are actually ordinary functions, such as
// Factory function func createNode(val int) *TreeNode{ return &TreeNode{val: val} } // After learning c + +, you can see a typical error here. TreeNode{val: val} is a local variable built in a function // Return the pointer of the local variable, and the program will hang up! // go language will not hang ~ local variables can also be returned to others root1.left.right = createNode(9) // This is also possible
3. Creation of structure (stack area)
- Since the address of the local variable is returned as discussed above, where does this TreeNode exist?
- For example, in c + +, local variables are allocated on the stack. As soon as the function exits, the local variables will be destroyed immediately
- If you want to pass it out, you need to allocate it on the heap and release it manually. This is c + +.
- In java, generally, objects are in the heap and variables are in the stack.
- In go language:
- No need to know ~ because there is a garbage collector. It needs to be determined according to the editor of go language and the running environment.
- As in the above example, when the compiler finds that TreeNode{val: val} does not take an address and returns, it feels that it does not need to be used for the outside world and may be allocated in the temporary stack.
- When an address is taken (like the example above) and returned, it will be allocated on the heap. After allocation on the heap, it will participate in garbage collection. When the pointer passed to the outside world is "thrown away", the structure object will be recycled.
- Memory structure diagram
- [the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-ponh9NUs-1635157982893)(./struct.png)]
4. Define method for structure
// TreeNode structure type TreeNode struct { left, right *TreeNode val int } // Define method for structure func (node TreeNode) print() { // (node TreeNode) recipient fmt.Println(node.val) } // call root1.print()
-
There is a receiver, which is node TreeNode.
-
In fact, the definition of go language is similar to the ordinary function method. It is just a syntax. In fact, it is an ordinary function, which is equivalent to:
func print(node TreeNode) { // Here is value transfer, go only value transfer fmt.Println(node.val) } // call print(root1)
-
It should be noted that the above writing methods are value transfer. Internal modification of the function will not affect external objects of the heap, which is equal to copy.
- It needs to be written as follows:
func (node *TreeNode) print() { // Pass pointer fmt.Println(node.val) } // Call: This is more humanized. Although it becomes a pass pointer, the call remains unchanged. The actual call will automatically pass in the address root1.print() // Similarly, if the function definition is to pass a value, you pass in an address ~ and it will not affect it. It will automatically take out and copy the value from the address.
-
Summary: go is very smart. You know whether you want a value or a pointer when calling a function.
- Nil pointer can also call methods! It is different from other languages because go sometimes initializes empty objects and sometimes nil. But it can be used.
type TreeNode struct {
left, right *TreeNode
val int
}
//Define method for structure
Func (node * treenode) print() {/ / (node treenode) receiver
fmt.Println(node.val)
}
func (node *TreeNode) setValue(val int) {
if node == nil {
fmt.Println("Setting val to nil node!")
}
node.val = val
}
var pRoot *TreeNode
//nil pointer, calling method
pRoot.setValue(200) / / you can print it here, indicating that the method can be called, but the following node.val = val operation cannot be performed~
pRoot = &root1
pRoot.setValue(300)
pRoot.print()
* For the value receiver, the pointer receiver's problem * To change content, you must use a pointer receiver * If the structure is too large, consider using the pointer receiver * Consistency: if there are pointer receivers, it is better to be pointer receivers. * Value recipient is go Unique. Pointer receiver, c++, java quote, python(self),It's also like a pointer. ### 5. Implement a method of traversing binary tree * Preorder traversal: [Force buckle original question](https://leetcode-cn.com/problems/binary-tree-preorder-traversal/submissions/) ```GO /** * Definition for a binary tree node. * type TreeNode struct { * Val int * Left *TreeNode * Right *TreeNode * } */ // Recursive problem solving func preorderTraversal(root *TreeNode) (vals []int) { // This is equivalent to defining the variable vals var preorder func(*TreeNode) preorder = func(node *TreeNode) { if node == nil { return } vals = append(vals, node.Val) preorder(node.Left) preorder(node.Right) } preorder(root) return } // In addition, it should be noted that slice is initialized zxc := []int{} var cvb []int fmt.Println(zxc == nil, cvb == nil) // Result false, true
- The focus is on the nil pointer and calling the method. If the above method is implemented as a TreeNode method, in other languages, an error will occur when node == nil. Nil in go language can call the function under internal judgment.
// Method written as TreeNode func (node *TreeNode) traverse() { if node == nil { return } node.print() node.left.traverse() node.right.traverse() }
###6. Package and packaging
-
By naming the function, you can distinguish whether the function is visible or invisible. CamelCase is usually used for names: words are capitalized together.
-
For packages:
- Initial capital: public
- Initial lowercase: private
-
Package:
- One package per directory: the package name does not have to be the same as the directory name, but each directory can only have one package
- The main package contains executable entries and main functions. If there is a main function in the directory, there can only be one main package in this directory.
- The methods defined for the structure must be placed in the same package.
- You can make different files
tree catalogue node.go //Define method and node structure package tree traverse.go // For clarity, you can also separate the methods of traversing nodes into a separate file. Can be found entry // catalogue entry.go // Move the main function here, package main: there can only be one package in a directory. To create a new directory, name it package main, and write in the main function. // The import statement in entry.go imports the tree directory. // The import statement in entry.go imports the tree directory. // tree.TreeNode is required when creating variables var root tree.TreeNode
-
Something about the guide bag (I feel it):
- First, the package name is not necessarily the same as the directory name where the package is located.
- Then, when the package is imported, it is the imported directory name
// directory structure 01 testing retriever.go // The package name is package testing111, which defines a type Retriever struct {} downloader.go // Import the package and call the contents of the package import "01/testing" // Import to pathname testing111.Retriever{} // Call using package name
-
In addition, structures and methods defined in another package can only be defined in the same package, otherwise an error will be reported: invalid receiver *testing111.Retriever (type not defined in this package)
-
Create another file in the testing directory above. Similarly, the method can be successfully defined in package testing111 because it belongs to the same package.
7. Extend existing types
-
In other languages, there is the concept of inheritance, which can extend existing types
-
There is no inheritance in the go language
-
go can extend system types and other types in two ways:
-
The following code. According to the package structure defined above (tree directory)
-
- Use combination:
// (according to the go language habit, the package is called tree, and TreeNode is rewritten as node, tree. TreeNode, which is too repetitive) // And the initial capital letter indicates the method of Public. All method names are capitalized. type Node struct { Val int Left *TreeNode Right *TreeNode } // Objective: make Node objects support medium order traversal (Node implements pre order traversal) // Define your own node type MyTreeNode struct { node *tree.Node // Saves a pointer to the node type. } func (myNode *MyTreeNode) InOrder() { if myNode == nil || myNode.node == nil { // If it is empty, or the node of the package is empty, it will not proceed. return } // You need to wrap the Node as myTreeNode and call the middle order traversal method for middle order traversal left := myTreeNode{myNode.node.Left} right := myTreeNode{myNode.node.right} // myTreeNode{myNode.node.Left}.InOrder() cannot be written like this here because it passes a pointer and must be received with a variable left.InOrder() myNode.node.Print() right.InOrder() } // Display of error message: myTreeNode{myNode.node.Left}.InOrder() /* cannot call pointer method on myTreeNode literal cannot take the address of myTreeNode literal Cannot call pointer method on myTreeNode literal Cannot get the address of myTreeNode text */
-
-
- By alias
Implement a queue. // Define a Queue. The directory structure is as follows queue // catalogue entry // catalogue main.go // Program entry queue.go // Defining structures and methods
-
queue.go
package queue // Define a queue type Queue []int func (q *Queue) Push(v int) { // go language features, pointers can be changed. Q passes in a pointer, * q operates the object it points to // Then q points to the new object, and then points to the newly generated object of append // *Q is equivalent to self in python, representing the object itself, but in go, what self (q) points to is changed into a newly generated object ~! *q = append(*q, v) } // First in first out queue func (q *Queue) Pop() int { head := (*q)[0] *q = (*q)[1:] return head } func (q *Queue) IsEmpty() bool { return len(*q) == 0 }
- main.go
package main import ( "fmt" "01/queue" ) func main() { q := queue.Queue{1} q.Push(2) q.Push(3) fmt.Println(q.Pop()) fmt.Println(q.Pop()) fmt.Println(q.IsEmpty()) fmt.Println(q.Pop()) fmt.Println(q.IsEmpty()) }
- The print result is:
1 2 false 3 true // Meet expectations
8. Use inline to extend existing types
- Embedding: Embedded
// The previous combination: type MyTreeNode struct { node *tree.Node // Saves a pointer to the node type. } // Embedding is to remove node, which is equivalent to a syntax sugar of go. It saves some code and is simpler type MyTreeNode struct { *tree.Node // Saves a pointer to the node type. } // The advantage of embedding is to save code. Look at the code in the sequence traversal in the previous combination left := myTreeNode{myNode.node.Left} right := myTreeNode{myNode.node.Left} // After using embedded, myNode has all the properties and methods of the original Node, as well as its own defined properties and methods. // Write directly, myNode.Left, omitting the middle node left := myTreeNode{myNode.Left} right := myTreeNode{myNode.right}
- Summary: expand the system type or the type of others
- Defining aliases: simplest
- Use combination: most commonly used (no seamless conversion can be made between defining alias and using combination, and attention should be paid to later maintenance)
- Use inline: syntax sugar, save code. (poor readability)
9. Differences between extension and "inheritance" of other languages
-
Use inline
-
Rewriting in other languages is reflected in go. You can also define a method with the same name as TreeNode in myTreeNode. The prompt is: shadowed.
-
Directly calling the mynode. Method name will be calling the overridden method. However, the hidden mynode.TreeNode. Method name can call the original method.
-
There is a concept of polymorphism in java: the parent class and parent class reference point to the child class object (probably upper transformation):
The parent class object is not accessible, and the child class method.
The upper transformation object can access not only the parent method (called through super.), but also the member variables inherited or hidden by the subclass, or call the method inherited by the subclass (parent method) or the instance method overridden by the subclass. Furthermore, the reference of a parent type to an object of a subclass can not only use the powerful functions of the subclass, but also extract the commonness of the parent class.
- Go language can't do this ~, to achieve similar functions, go language comes through interfaces.
-
Chapter 5: dependency management of go language (package management)
1. Basic concepts
- Concept of dependency:
- Dependence has many meanings
- This refers to the extensive use of third-party libraries. We should build the functions on some infrastructure already implemented by others.
- For example, pull a library of others on github, pull the code of the library to the code tree, and build our code with the library, so that it can work. This is the focus of our concern (dependence).
- Dependency management has gone through three stages: GOPATH, vendor and go mod (mainstream).
2. GOPATH
-
This is the installation path of go
-
By default, ~ / go (UNIX, Linux),% userprofile% \ go (Windows) are all in the user directory
- GOPATH is managed without. When specifying GOPATH, if you need anything, I will look under GOPATH.
- Problem: all necessary or dependent libraries for the project are placed under the GOPATH directory. Will get bigger and bigger.
-
Practice GOPATH (remember to turn off GO111MOODULE first):
- Turn off: the whole system changes: go env -w GO111MOODULE = "off", current window: export GO111MOODULE = "off"
- GOPATH must also meet some directory conditions
// Create a directory called gopathtest as GOPATH // directory structure gopathtest src // This directory must have this name. Various codes can be placed under src // 1. go env -w GOPATH = "directory" changes the entire GOPATH of the system // 2. export GPPATH = "directory" temporarily changed. This terminal window only // Pull a project go get -u go.uber.org/zap // Library name zap // When you see it again, it will appear in the src directory go.uber.org
-
Write code test: zaptest.go
import "go.uber.org/zap" func main() { log, _ := zap.NewProduction() log.Warn("warning test") }
- Generally, there are two places to find bags:
-
- GOROOT: there is a src under the location where go is installed. Find it below
- Go down to the specified GPPATH
-
- Generally, there are two places to find bags:
-
Problem: for the zap Library in the above map, if two projects need to use this library, but the required versions are different, it can't be done
- Solution: in the project directory, create a vendor directory and put the zap library below, which will give priority to the use here.
// directory structure gopathtest src go.uber.org zap // zap Library project1 // Item 1 vendor go.uber.org // Put one on project2 // Item 2 vendor go.uber.org // Put one on
- You can't always get all dependencies and then manually copy them to the vendor, so there are some third-party dependency management tools
- glide, dep, go dep, etc
- The real operation is not to move the vendor directory, but to change the configuration files of various management tools. After the settings are clear, let the management tools complete the copy.
3. go mod usage
-
- Basic usage and introduction:
-
When you open GO111MOODULE, download this package again: go get -u go.uber.org/zap
- You can specify the version go get -u go.uber.org/zap@v1.12.0
- Then the code structure is as follows:
gomodetest // Create project name go.mod go.sum // For introduction, please refer to: https://my.oschina.net/renhc/blog/3171035 zaptest.go // Test code
- go mod content:
module gomodetest go 1.17 require go.uber.org/zap v1.19.1 require ( go.uber.org/atomic v1.7.0 // indirect go.uber.org/multierr v1.6.0 // indirect )
- zaptest code is the same as above
- The version of the call is specified.
- Zap is installed in go/pkg/mod: C:\Users\Administrator\go\pkg\mod\go.uber.org\zap@v1.19.1
- command
- go mod init + module name to generate mod file
- go mode tidy to delete unused modules or add modules that are used but do not exist in go.mod
- You can find it on the Internet. The current cognition can only take these notes on go mod, but the understanding is not deep enough.
- If you want to change the version, you can directly modify go mod. For example, you can use version 1.12.0. After the modification, go mod tidy can be used. Open the corresponding directory and you will find both versions in it.
- Two ways to generate good go mod. One is to go get... And the other is to run the program to help you get automatically according to imoprt
-
- Migrate old projects to go mod
-
- go mod init + module name (or others) to generate mod files.
- go build. /..., every time you encounter import, you will pull the required libraries and put them into a local unified cache.
- If some management tools mentioned in GOPATH have been used in the project, mod may be generated according to their configuration information during init.
4. Summary go mod
- It is managed by the go command. Users do not have to care about the directory structure. Unlike before (GOPATH), their projects (project1 and project2 must be placed in the GOPATH src directory). With vendors, it is more complex. Their projects must also have vendor directories.
- Initialization: go mod init
- Add dependency: go get, or write code directly. Dependency will be automatically pulled and added during build
- Update dependency: go get, @v specify the version, go mod tidy remove redundant or add unnecessary.
- Migration project: go mod init, go build. /
5. Catalogue arrangement
- As mentioned before, a file directory can only have one package, and a package can only have one main function.
// A directory structure like this: 01 arrays.go maps.go slices.go funcs.go // 01 is a project I built. Each of the following is a file I created to learn the basic knowledge of each part. When executed go build + Any file can be compiled, but when executed go build, Errors occur when all files are compiled at once: main redeclaration in this block : Obviously, there is one under each file mian Function is wrong.
- There is no good solution. Sort each main function into different files, as shown in the following directory structure
// Put each file in a separate folder to have a separate main function. 01 arrays arrays.go maps maps.go slices slices.go funcs func.go
- Now you need to execute: go build. /... To compile. Build and compile the files in the current directory (empty) and all subdirectories.
- This is indeed a little cumbersome, but it is like this ~, and so is the official toolkit tools of go language. A command, placed in a folder. More engineering and clearer code structure.
- (I don't understand it at the moment) there will be compiled result files after compilation. Generally, half of the compilation fails in this directory. Compiled successfully and placed in the bin directory under GOPATH.
- Compile multiple packages at a time and there will be no results. It can be generated by go install. /.
Chapter 6: Interface
// Take a look first, and then come back to this structure. Python also has this "duck type". It feels like go here is changing "static type" into "dynamic type". // This interface does not need to consider what Traversal refers to, and whether the interface is implemented (not in java. go) as long as the Traverse() method is satisfied. It can be it. // "It feels like you don't need to determine the type when defining variables, but dynamically determine it at run time ~," getTraversal() passes what type to meet the method defined by the interface, and the interface can be that type. type Traversal interface { Traverse() } func main() { traversal := getTraversal() traversal.Traverse() }
1. Outgoing interface
- Realize the decoupling of main function and function function function. The main function only needs to call the function function, not to implement the function
Write a file: downloader.go package main import ( "fmt" "io/ioutil" "net/http" ) // Define a function that requests a link func retrieve(url string) string { resp, err := http.Get(url) if err != nil { panic(err) } defer resp.Body.Close() bytes, _ := ioutil.ReadAll(resp.Body) // To string output return string(bytes) } func main() { fmt.Println(retrieve("https://www.imooc.com")) }
- The coupling has been reduced here, but the main function must call the retrieve() function, which increases the coupling.
- (suppose you are writing a project) let's assume that the function of main() is completed by a department. retrieve is done by another department (infra).
// Current directory structure downloader.go infra // Under the infra package, there is a method to parse the url urlretriever.go
-
urlretriever.go:
package infra import ( "io/ioutil" "net/http" ) // To introduce an interface, first define it as a structure type Retriever struct {} func (Retriever) Get(url string) string{ // You don't need to name the recipient because you can't use it resp, err := http.Get(url) if err != nil { panic(err) } defer resp.Body.Close() bytes, _ := ioutil.ReadAll(resp.Body) // To string output return string(bytes) }
-
downloader.go :
package main import ( "fmt" "01/infra" ) func main() { retriever := infra.Retriever{} // Build an object and call it fmt.Println(retriever.Get("https://www.imooc.com")) }
-
A new problem: the phrase "retriever: = infra.Retriever {}" does not always call infra.Retriever {}. It does not belong to a logic, but more like a configuration. Extract it.
dowloader.go It's written in Chinese func getRetriever() infra.Retriever { return infra.Retriever{} } func main() { retriever := getRetriever() // Get the structure object through the method fmt.Println(retriever.Get("https://www.imooc.com")) }
-
New problem: in this way, there is still a strong coupling, and the type of retriever cannot be changed. He has always been of type infra. Retriever. Retriever: = getRetriever() is always equivalent, so var retriever infra.Retriever = getRetriever() is of type infra.Retriever. If we have another team (testing), they also have a function to parse URLs. Then, it is very troublesome to call this testing.Retriever. You need to change the getRetriever method to testing.Retriever type from beginning to end
// A new directory structure is added to the testing department downloader.go infra // Under the infra package, there is a method to parse the url urlretriever.go testing retriever.go
-
retriever.go:
package main type Retriever struct{} func (Retriever) Get(url string) string { return "fask content" // The implementation content is different from that of infra department }
-
Then the final modification: (export the interface concept)
package main import ( // "01/infra" "fmt" "01/testing" ) // The return value is an interface type and must have a Get method func getRetriever() retriever { // Return type retriever return testing.Retriever{} } // Define interface type retriever interface { // Get Get(string) string } func main() { var r retriever = getRetriever() fmt.Println(r.Get("https://www.imooc.com")) } // At this time, it is completely decoupled. We only need to change the return value in the getRetriever() method to infra.Retriever {}, and then we can call go in infar without moving anything else.
- Interface is an abstract concept, no matter what it is. The above definition of it is that as long as it can call the GET method.
- So you can assign it to testing.Retriever {} or infra.Retriever {}
- If we define the method in the interface as GET1, an error will be reported when running again:
- testing.Retriever does not implement retriever (missing Get1 method)
- The interface in go is different from java. After learning java, you may wonder that the testing.Retriever {} object does not implement the interface? How can I use it? This is where the go language is special and advanced: duck typing.
2. duck typing
-
As I said before, go is not like an object-oriented language. It only supports encapsulation, such as inheritance and polymorphism. What needs to be done is done by using interfaces instead in go. Therefore, the interface in go language is much more flexible than that in other languages.
-
Duck type: simply put: "walk like a duck, bark like a duck (look like a duck), then he is considered a duck". Different people have different perceptions of things. Whether it is a "duck", from my point of view, to meet my needs, I think it is a "duck". In programming: as long as you can provide the capabilities (Methods) defined in my interface, I think you are a "duck". To meet my needs, I can assign values to your type and interact with these capabilities.
-
In addition, go language is a structured type system, similar to duck typing, because when duck typing was mentioned, it must be bound dynamically! (refer to Python interpretive language duck type), but go language is bound at compile time ~ it can only be similar. See here, you can understand the opening paragraph and code.
-
duck typing in Python
[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-SG8CZ2og-1635157982895)(.\py_duck.png)]
- **duck typing in C + + * *
[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-nND91X9P-1635157982896)(./c++_duck.png)]
- java has no duck typing, only similar code
[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-if6uPutl-1635157982898)(./java_duck.png)]
- duck typing in go
[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-Ol3j3bmA-1635157982898)(./go_duck.png)]
###3. Definition and implementation of interface
- The interface of go is user-defined.
- In the previous example, the former is the user and the latter is the definer. download uses the Get method of the retriever. download defines an interface with a Get method.
- The implementation of the interface is implicit. As long as the method defined in the interface is implemented, the type (Definer) even implements the interface
// directory structure retriever mock mockretriever.go //Definer main.go // User
- main.go: Caller
package main import ( "01/retriever/mock" "fmt" ) type Retriever interface { Get(url string) string } func download(r Retriever) string { return r.Get("www.imooc.com") } func main() { var r Retriever r = mock.Retriever{"this is me"} fmt.Println(download(r)) }
- mockretriever.go
package mock type Retriever struct{ Contents string } // Implement the Get method. (because of the user, the definer implements the Get interface by default) func (r Retriever) Get(url string) string { return r.Contents }
###4. Value type of interface
- According to the above example, r represents the value type. In other languages, it may be just a reference or a pointer. But all in the go language are value types. r doesn't have a pointer inside. It's so simple. There are other things.
var r Retriever fmt.Printf("%T, %v\n", r, r) r = mock.Retriever{"this is me"} fmt.Printf("%T, %v\n", r, r) // Print results <nil>, <nil> mock.Retriever, {this is me} // r has a type and a value inside // Change function Get to receive pointer func (r *Retriever) Get(url string) string { return r.Contents } var r Retriever fmt.Printf("%T, %v\n", r, r) r = &mock.Retriever{"this is me"} // Get address fmt.Printf("%T, %v\n", r, r) // Print results <nil>, <nil> *mock.Retriever, &{this is me} // All pointers Value passing is mock.Retriever{"this is me"} An object is generated and copied to r In the stomach. Pointer passing is to put the pointer of an object into r In the stomach.
-
View type of r
- Method 1
// switch can get the type of r switch v := r.(type) { case *mock.Retriever: fmt.Println(v.Contents) }
- Method 2: Type assertion
// Type assertion mockRetriever, ok := r.(*mock.Retriever) // When it is a value, it is r.(mock.Retriever) // ok is used to determine whether the type is correct fmt.Println(ok, mockRetriever.Contents)
-
Interface variables (such as r)
- There is the type of implementer, the value of implementer (or the pointer of implementer, pointing to an implementer)
- Interface variable with pointer
- Interface variables are also passed by value, and there is almost no need to use the pointer of the interface (because there is a pointer in the belly of the interface variable).
- The pointer receiver implementation can only be used in pointer mode; Value recipients can
-
Any type: interface {}
q := []interface{} {1, "ahah", '1'} // q becomes, like a list in Python. Can receive any type.
5. Combination of interfaces
- First:
// Combine two existing interfaces type RetrieverPoster interface { Retriever // Implement Get Poster // Implement Post } s := &mock.Retriever{"this is me"} // Define another Post method in mock.Retriever func session(s RetrieverPoster){ s.Get() s.Post() }
- Second:
// Direct definition interface type Rf interface { Post(url string) string Get() }
- For example, the io.go file on the official website provides many combined interfaces: readable, writable, readable and writable, etc.
// Partial code type Seeker interface { Seek(offset int64, whence int) (int64, error) } // ReadWriter is the interface that groups the basic Read and Write methods. type ReadWriter interface { Reader Writer } // ReadCloser is the interface that groups the basic Read and Close methods. type ReadCloser interface { Reader Closer } // WriteCloser is the interface that groups the basic Write and Close methods. type WriteCloser interface { Writer Closer }
6. Common system interfaces
-
stringer: equivalent to tostring in java
fmt/print.go There is one below stringer. type Stringer interface { String() string } // As long as our own object implements the method (String ()), we can implement the interface. fmt.Println(object) // Will be called.
-
Reader
io/io.go There is one below Reader, Writer type Reader interface { // Realize the function of reading files Read(p []byte) (n int, err error) } type Writer interface { // Realize the function of writing files: byte, slice and string can be used as readers and writers Write(p []byte) (n int, err error) } Abstraction, any implementation and Read, Write The objects of the method implement these two interfaces and can be read and written.
-
Writer
Chapter 7: functions and closures (functional programming)
###1. Basic concepts
- Functional programming VS function pointer
- Functions are first-class citizens: parameters, variables, and return values can all be functions
- In c + +, there are only function pointers. In java, functions are just names. There is no way to pass names to others
- Functions can be passed as parameters in python.
- "Orthodox" functional programming
- Immutability, no state, only constants and functions. Neither selection statement nor loop statement can be used
- Function has only one argument
- go and python are not really functional programming ~ the usage is more flexible
package main import "fmt" // Realize accumulation func adder() func(int) int { sum := 0 var a func(i int) int = func (i int) int { sum += i return sum } return a } func main() { a := adder() for i := 0; i < 10; i++ { fmt.Println(a(i)) } } // The function inside can only be written like this 1. var a func(i int) int = func (i int) int , Define a variable to receive 2. More concise use of anonymous functions, save the name func adder() func(int) int { sum := 0 return func (i int) int { sum += i return sum } }
-
The above example demonstrates the concept of closure (related concepts have been learned in Python)
- The inside of a function is called a local variable
- If it is outside function a but inside function adder, and function a is also defined inside function adder, sum is called a free variable. (closure namespace in Python)
-
Closure in Python
[external chain picture transfer failed. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-hLAivqUt-1635157982899)(./py_Closure.png)]
-
Closures in c + + (relatively new syntax, after c++ 14)
[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-Gqe5ou6e-1635157982900)(./c++_Closure.png.png)]
-
The definition in java should be stricter: use the final keyword to define a class. holder, there is only one value in it
-
Functions in java are not first-class citizens and do not support functions as parameters and return values, so an object is used to simulate a function.
[the external chain picture transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the picture and upload it directly (img-tvLjX2Oz-1635157982901)(./java_Closure.png)]
Example 1: Fibonacci
- Realize fibonacci
package main import "fmt" func fibonacci() func() int { a, b := 0, 1 return func() int { a, b = b, a+b return a } } func main() { f := fibonacci() for i := 0; i < 10; i++ { fmt.Println(f()) } }
Example 2: implementing interfaces for functions (continued fibonacci).
- Similar to file printing (io.Reader in the system interface mentioned above), implement the printing interface for fibonacci and print the number of fibonacci. (it's difficult. I've understood it for a long time. I have to watch the video carefully for many times, look back again and again, and understand it in series. It involves a lot of knowledge)
package main import ( "bufio" "fmt" "io" "strings" ) func fibonacci() intGen { a, b := 0, 1 return func() int { a, b = b, a+b return a } } type intGen func() int // Function type is a flexible place where a type can implement interface ~~ go language // It's actually a kind of grammar sugar, as mentioned earlier. // (g intGen), which is the g.Read() call. If you don't write this, it is a Read(g) call. No, very special, just ordinary functions // Therefore, even function objects can implement interfaces (implementing methods, which are used by users, is equal to implementing interfaces) func (g intGen) Read(p []byte) (n int, err error) { // n: How many bytes are written? error exception // Next calls g to read the next Fibonacci number next := g() // Control greater than 10000 stops if next > 10000 { return 0, io.EOF } // Call Sprintf, calculate the corresponding number of bytes, convert it to string, add line feed, and save it in p s := fmt.Sprintf("%d\n", next) // Proxy write p operation through NewReader return strings.NewReader(s).Read(p) // strings.NewReader(s) returns a Reader object. It calls the Read() method to write s to p } func printFileContents(reader io.Reader) { scanner := bufio.NewScanner(reader) // Click the source code to find that io.Reader is packaged as a Scanner object for scanner.Scan() { // Loop judgment, Scan() Text() are the methods provided by the Scanner object for output fmt.Println(scanner.Text()) } } func main() { f := fibonacci() // The printFileContents function defines the parameter reader as the io.Reader interface type. So you need to f implement the reader method // Then, the printFileContents function continuously calls the Read method to write data and then Read. // And Read internally implements a function of adding a Fibonacci number to p each time. printFileContents(f) // The overall process is as follows: 1. Pass the f object as a parameter to the printFileContents() function // 2. The io.Reader interface type variable is used internally to receive F, and f implements the reader method // 3. Continuously call next: = go() internally to write the next Fibonacci number for printing. // Difficulties. 1. Understanding of the implementation of the io.Reader interface; 2. Implementing the Reade method for a function type (indirectly, implementing the io.Reader interface) }
Example 3: use function to traverse binary tree
package main import "fmt" // Define node type TreeNode struct { left, right *TreeNode val int } // Medium order traversal func (node *TreeNode) TraverseFunc(f func(*TreeNode)){ // Pass a function if node == nil { return } node.left.TraverseFunc(f) f(node) node.right.TraverseFunc(f) } func main() { root1 := TreeNode{val: 3} // Cumulative counting of nodeCnt nodeCnt := 0 // Pass anonymous functions to realize cumulative counting, and count the number of nodes in order root1.TraverseFunc(func (node * TreeNode){ nodeCnt ++ }) fmt.Println(nodeCnt) }
- The closure of go language is more natural (Python has closure space, so it is not clear about the variable management of go. Python needs to use nonlocal declaration, but go does not)
- go does not have an anonymous Lambda expression, but has an anonymous function. Anonymous function is enough. It is required to be concise. There is no need to add syntax to realize Lambda expression.
Chapter 8: resource management and error handling
- resource management
- For example, opening a file requires closing it
- To connect to the database, you need to release
- They all appear in pairs, but as soon as they are mixed with error handling, they become more complex. If the program is abnormal in the middle, how to ensure that the open link is closed? This is resource management and error handling
1. defer call
-
Ensure that the call occurs at the end of the function
-
Defer list and meet the requirements of first in and last out (that is, the order in which defer statements appear, first appear and then execute)
-
Parameters are evaluated at the time of a defer statement
func tryDefer() { defer fmt.Println(1) // Output 2 first, then output 1 fmt.Println(2) } func tryDefer() { defer fmt.Println(1) defer fmt.Println(2) fmt.Println(3) } // Ensure first in and last out. So the print result is 3 2 1 func tryDefer() { defer fmt.Println(1) defer fmt.Println(2) fmt.Println(3) return // panic("error") this does not affect, and the output result is still 3 2 1. Even if the following program is interrupted, it still does not affect the expected execution fmt.Println(4) }
-
Write a file in practice (create a file and write fibonacci sequence)
- Resource management of go language: create a file -- > close the file. Write cache -- > flush() appears in pairs, so we can directly defer close() after opening the file
package main import ( "01/functional/fib" "bufio" "fmt" "os" ) func writeFile(filename string) { file, err := os.Create(filename) if err != nil { panic(err) } defer file.Close() // Close file on completion // Writing files directly is slow, using bufio writer := bufio.NewWriter(file) // The buffer buffer is written to the memory first, and when it reaches a certain size, it is written to the hard disk at one time defer writer.Flush() // Only the buffer buffer is written and needs to be imported f := fib.Fibonacci() for i := 0; i < 20; i++ { fmt.Fprintln(writer, f()) } } func main() { writeFile("fib.txt") }
-
Parameters are evaluated during the defer statement:
func forDefer() { for i := 0; i < 100; i++ { defer fmt.Print(i, " ") if i == 30 { panic("error") } } } The print result is: // 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 panic: error // Output 30 first, and then output 29.. 28.. 0 to meet the requirements of first in first out. Moreover, instead of printing 30 30. Description i parameter values, they have been calculated when the program executes the defer statement.
-
A super interesting example:
package main import "fmt" func f1() int { x := 5 defer func() { x += 1 }() return x } func f2() (x int){ defer func() { x++ }() return 5 } // The output is 5, 6
- The analysis doesn't understand, but according to the first example: pass a pointer instead
func f1() *int { x := 5 defer func() { x += 1 }() return &x } a := f1() fmt.Println(*a) // The result is 6. It can only be said to be value transfer. return returns the value 5. x, right after it++
- Brief analysis: defer is indeed the second one executed after return. Because you defined x when you return, you can add it. The first one returns a value of 5.
2. Error handling
-
In fact, the error handling of go language is mainly around error. For example, file, err: = OS. Create (filename).
- Judge this err. Do some wrong handling.
- For example, file reading and writing.
func writeFile(filename string) { file, err := os.OpenFile(filename, os.O_EXCL|os.O_CREATE, 0666) // 0666 linux file permissions, added this os.O_EXCL, the file cannot be opened if it exists. Artificially create an error exception if err != nil { // panic(err) you can't simply panic and interrupt the program here. Detailed error handling can be carried out // 1. Print out and return. Do not let exceptions interrupt the program // fmt.Println("file already exists ", err) // return // 2. Open the source code of OpenFile() function and you will find that if there is an error, it will be of type * PathError. Err is the interface type and implements the Error() method. If an error is reported here, it will return * PathError if pathError, ok := err.(*os.PathError); !ok { // Type assertion to see if an error of this type will be reported panic(err) // When the type of * os.PathError is not sent in, panic is sent directly } else { fmt.Println(pathError.Op, // Output some information about pathError pathError.Path, pathError.Err) } return } // Print result: open fib.txt The file exists. Corresponds to the above three prints.
- You can also customize Error: err = errors.New("this is a custom error"), or define an Error method to implement the interface.
3. Server unified error handling
-
How to implement unified error handling logic
- Implement a webserver that displays files and access the fib.txt file generated above
- File directory
errhandling defer fib.txt // fib file filelisting web.go // webserver, accessing fib files
-
Initial version (implementation logic)
// web.go package main import ( "fmt" "io/ioutil" "net/http" "os" ) // Implement a web server that displays files func main() { http.HandleFunc("/list/", func (writer http.ResponseWriter, request *http.Request) { // Get the specific file name or directory behind the list fileName := request.URL.Path[len("/list/"):] // /list/fib.txt, through slicing, as long as the back. fib.txt // Open file fibPath := fmt.Sprintf("%s%s", "../defer/", fileName) // Get the file name and splice the path file, err := os.Open(fibPath) if err != nil { panic(err) } defer file.Close() // read file all, err := ioutil.ReadAll(file) if err != nil { panic(err) } // Write the content to the ResponseWriter writer.Write(all) }) err := http.ListenAndServe(":8888", nil) if err != nil { panic(err) } }
- You can try to access: http://localhost:8888/list/fib.txta , you will get an error message
- http: panic serving 127.0.0.1:60714: open .../defer/fib.txta: The system cannot find the file specified.
- The os.Open function will report an error and panic interrupts the program, but it is not completely interrupted. A bunch of error prompts are printed, and then if you enter the correct url at this time, you will get the correct prompt. The server does not stop (generally, panic interrupts the whole program, but in HTTP server, panic does not interrupt the program)
- You can try to access: http://localhost:8888/list/fib.txta , you will get an error message
-
Attempt to process error:
- Remove the panic for treatment
file, err := os.Open(fibPath) if err != nil { http.Error(writer, err.Error(), http.StatusInternalServerError) // code return } // Finally, execute the wrong url, and the result will be displayed in the browser without printing a bunch of error messages in the background // Open.. / defer / fib.txta: the system cannot find the file specified
- Disadvantages: this will display the internal error information to the user.
-
Wrap these error messages into an external err.
- Look at the new directory structure:
filelisting listing handeler.go // Extract the logic of function processing web.go // mian letter
- Take out the anonymous processing function required by http.HandleFunc(), put it in a separate file (handler. Go), and only implement the main logic, return all errors and hand them over to the outside for processing.
- handeler.go: handle logic normally, and directly return an error when encountering a problem.
package listing import ( "fmt" "io/ioutil" "net/http" "os" ) func HandFileList(writer http.ResponseWriter, request *http.Request) error { // Get the specific file name or directory behind the list fileName := request.URL.Path[len("/list/"):] // /list/fib.txt, through slicing, as long as the back. fib.txt // Open file fibPath := fmt.Sprintf("%s%s", "../defer/", fileName) file, err := os.Open(fibPath) if err != nil { return err } defer file.Close() // read file all, err := ioutil.ReadAll(file) if err != nil { return err } // Write the content to the ResponseWriter writer.Write(all) return nil }
- web.go: define a function type appHandler.
- Define an errWrapper() function. Pass the processing function as a parameter and handle internal errors uniformly.
- It seems very complex, but it is actually unfamiliar with the semantics of go language. In vernacular, handeler.go is a function that defines normal processing logic. Then we define a function. The return value of this function is a function. The internal call of the function completes the business processing function of handler.go. The returned exception is accepted. The following exceptions are handled and judged.
package main import ( "01/errhandling/filelisting/listing" "net/http" "os" ) // Define a function type and return error type appHandler func(writer http.ResponseWriter, request *http.Request) error // Define another function to return the function required by the HandleFunc() parameter func errWrapper( handler appHandler) func( http.ResponseWriter, *http.Request) { return func(writer http.ResponseWriter, request *http.Request) { err := handler(writer, request) if err != nil { code := http.StatusOK switch { // If the file does not exist case os.IsNotExist(err): http.Error(writer, http.StatusText(http.StatusNotFound), http.StatusNotFound) code = http.StatusNotFound // No authority case os.IsPermission(err): code = http.StatusForbidden // default default: code = http.StatusInternalServerError } http.Error(writer, http.StatusText(code), code) } } }
//Implement a web server that displays files
func main() {
http.HandleFunc("/list/", errWrapper(listing.HandFileList)) / / process logic functions and abstract them
err := http.ListenAndServe(":8888", nil)
if err != nil {
panic(err)
}
}
4. panic and recover
-
panic
- Stop the execution of the current function (I think it's a bit like raise in Python)
- Always go back up and execute the defer of each layer
- If recover is not encountered, the program exits
- Panic (English: panic, hate). That is, when the program encounters problems, it is very panic, very angry and doesn't know what to do. So panic is a very heavy word. Generally speaking, try to use as little as possible.
-
recover
- Used only in defer calls
- You can get the value of panic
- If it cannot be processed, the panic can be reset
package main import ( "fmt" ) func tryRecover() { defer func() { r := recover() // func recover() interface{} . You can see that recover () is of any type if err, ok := r.(error); ok { fmt.Println("Error occurred: ", err) } else { panic(r) } }() // Define an anonymous function and call b := 0 a := 5 / b fmt.Println(a) } func main() { tryRecover() } // result Error occurred: runtime error: integer divide by zero // The program does not terminate abnormally
-
If the direct panic(123) is not an exception type, the panic is also sent out 123
func tryRecover() { defer func() { r := recover() // func recover() interface{} . You can see that recover () is of any type if err, ok := r.(error); ok { fmt.Println("Error occurred: ", err) } else { panic(r) } }() // Define an anonymous function and call // b := 0 // a := 5 / b // fmt.Println(a) panic(123) } // The value of r is 123
5. Server unified error handling 2
-
After learning the above panic and recover, let's take a look again
- If the person who writes the function processing logic above is not the same as the main function, it is like this to join the main
http.HandleFunc("/", errWrapper(listing.HandFileList)) // Change the original / list / to/
- Then the handler function in handeler.go is still processed as / list /
fileName := request.URL.Path[len("/list/"):]
- When input http://localhost:8888/abc When, an error will be reported, and the terminal will output a lot of error information! And it shows that the web page cannot be accessed normally ~ (this is not what we want) because the slicing operation exceeds the index length. Display some error information
2021/10/07 17:17:34 http: panic serving [::1]:65487: runtime error: slice bounds out of range [6:4] goroutine 6 [running]: net/http.(*conn).serve.func1(0xc00005ab40) D:/software/go/go1.15/src/net/http/server.go:1801 +0x147 panic(0x66a120, 0xc0000aa080) D:/software/go/go1.15/src/runtime/panic.go:975 +0x3e9 01/errhandling/filelisting/listing.HandFileList(0x6d9200, 0xc0000ae0e0, 0xc0000a8000, 0x0, 0x0)
- At this time, the server did not hang up. When the correct address is entered, it can still be accessed successfully. This is the * * protection * * of httpserver. At D:/software/go/go1.15/src/net/http/server.go:1801 +0x147 here:
func (c *conn) serve(ctx context.Context) { c.remoteAddr = c.rwc.RemoteAddr().String() ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr()) defer func() { if err := recover(); err != nil && err != ErrAbortHandler { const size = 64 << 10 buf := make([]byte, size) buf = buf[:runtime.Stack(buf, false)] c.server.logf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf) } if !c.hijacked() { c.close() c.setState(c.rwc, StateClosed) } }()
- You can see that a recover () is performed, err: = recover (), and then some processing is performed.
-
Then our code func errWrapper also needs to add a defer, recover. Operate when the program terminates abnormally.
func errWrapper( handler appHandler) func( http.ResponseWriter, *http.Request) { return func(writer http.ResponseWriter, request *http.Request) { // !!! Add defer to handle exceptions defer func() { if r != nil { r := recover() log.Printf("Panic: %v", r) http.Error(writer, http.StatusText(http.StatusInternalServerError), // Return server error status code http.StatusInternalServerError) } }() err := handler(writer, request) if err != nil { code := http.StatusOK switch { // If the file does not exist case os.IsNotExist(err): http.Error(writer, http.StatusText(http.StatusNotFound), http.StatusNotFound) code = http.StatusNotFound // No authority case os.IsPermission(err): code = http.StatusForbidden // default default: code = http.StatusInternalServerError } http.Error(writer, http.StatusText(code), code) } } }
- Then we'll visit again http://localhost:8888/abc When, the browser will display: Internal Server Error. The background terminal can only print: 2021/10/07 17:38:51 Panic: runtime error: slice bounds out of range [6:4]
-
There are also main contents in the post sequence. I don't understand them very much. I'm beyond the foundation. I don't understand these methods. Give up first.
Chapter 9: testing
- Skip first
Chapter 10: Goroutine
-
goroutine
- Lightweight "threads"
- Non preemptive multitask processing, and the cooperative process takes the initiative to hand over the control
- Multitasking at compiler / interpreter / virtual machine level
- Multiple coroutines may run on one or more threads
-
goroutine model:
- goroutine does not correspond to physical threads one by one. It is a lot of map s to a physical thread. It will be mentioned below that even if 1000 goroutines are opened, the corresponding threads will only be opened according to the number of cores of the physical machine. Because physical threads are managed by the operating system. The overhead is relatively large and uncontrolled. Physical threads are preemptive multitasking.
[the external link image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-nkzj62Vm-1635157982902)(./goroutine.png)]
-
First sight
package main import ( "fmt" "time" ) func main() { for i := 0; i < 10; i++ { go func(i int) { for { fmt.Printf("Hello from goroutine %d\n", i) // Printing is an io operation. This sentence will block and hand over control } }(i) } // Sleep for 10 seconds. The main program and the open collaboration are executed together. When the main program is executed and the coroutine is not executed, the main() function ends and the whole program ends time.Sleep(time.Millisecond*100) }
- go keyword starts a Coroutine to execute a task by default
- In learning python, we have implemented the contents of relevant collaborative processes:
- 1. Generator coprocess scheduler: encapsulate the generator into a coprocess (through the decorator), and schedule the execution of tasks through the coprocess scheduler
- 2. Collaboration scheduler, epoll: event driven tcp network server, collaboration scheduler. The socket is encapsulated as a co process adapter to realize the co process scheduler (eventloop), event driven programming is applied in the scheduler, and the co process tcp network server is realized.
-
Coroutine:
- lightweight thread
- For non preemptive multitasking, the cooperative process takes the initiative to hand over control. (decide for yourself)
- Multitasking at the compiler (go) / interpreter / virtual machine level. (go compiler level has its own scheduler, which does not depend on operating system scheduling)
- Multiple coroutines may run on one or more threads.
-
Change the above program to an array (if there is no operation such as io, there will be no switching and give up control)
package main import ( "fmt" "runtime" "time" ) func main() { var a [10]int for i := 0; i < 10; i++ { go func() { for { a[i]++ runtime.Gosched() } }() } // Sleep for 10 seconds. The main program and the open collaboration are executed together. When the main program is executed and the coroutine is not executed, the main() function ends and the whole program ends time.Sleep(time.Millisecond) fmt.Println(a) }
-
Two data conflicts: use go run -race goroutine.go to view data conflicts
- The execution of the main function is also a coroutine. The inner function is equal to using the outer closure variable i. It is likely that i in main will not be used in the function until i reaches 10, which will exceed the maximum length of the array. So pass i inside the anonymous function.
- Finally, main prints the array, but other coroutines may still write data to the array ~ this is another conflict, which needs to be handled by channel.
-
Any function can be sent to the scheduler for execution by adding go
- Distinguish whether it is an asynchronous function when it does not need to be redefined (async def for Python)
- The scheduler will switch at the appropriate point
- Use - race to detect data access conflicts
-
goroutine possible switching points (non preemptive)
- I/O, select
- channel
- Waiting lock
- Function call (sometimes)
- runtime.Gosched()
- The above is only for reference, and there is no guarantee that it will not be switched in other places
-
If 1000 coprocesses are opened continuously, the go language will open about seven threads, and the scheduler will only have a corresponding part of the threads active according to the number of cores of your machine. (top command can be viewed in linux system)
Chapter 11: Channel
1. channel bidirectional channel
-
Channel: bidirectional channel between goroutine and goroutine.
- Creation method:
var c chan int // c == nil a := make(chan int) // It can be written as a: = make (Chan, int, 3) plus cache and specify space
- channel is the same as a function. It is a first-class citizen and can be used as a function parameter and return value.
- var channels [10]chan int, which defines an array. Each element is a channel
- In addition, as a function return value
func createWorker(id int) chan int { // Create channel in function c := make(chan int) // You must create a coroutine to handle the channel. If you don't define the for loop into func, the program will be dead loop! Waiting to receive data. go func() { for { fmt.Println(<- c) } }() // Return to channel return c } // The createWorker function is used to create and return a channel. He didn't do anything. The real processing is in the internal func. // It can also specify the direction, and the external person can only receive the arrow sign func worker(id int) chan <- int {}
-
C: = make (Chan, int, 3) specifies the size and creates a buffer, which is helpful for performance. Three values can be imported cumulatively before switching and being received by other processes.
-
You can also stop sending: close(a) a is channel. This is the use of close() for sending and sending. The receiver can also receive data, but all received are 0 (a chan int).
- The receiver can judge whether the sender has called close() to receive data. Use n, ok: = < - C (c chan int). Use ok to judge whether it is close()
- In a more efficient way, you can use the range loop directly.
for n := range a { // range will automatically determine whether the sender calls close() fmt.Println(n) }
-
Theoretical basis of channel: Communication Sequential Process (CSP)
-
Don't communicate by sharing memory; share memory by communicating.
-
Do not communicate through shared memory; Share memory through communication.
-
2. Use channel to wait for the task to end
-
In the previous example, in the main program, time.Sleep() is used to wait. It is written as a task outside the mian function for execution. This method is not very good. We need a professional way to wait for the end of the channel task, and then end the main() function.
- In the above example, we can send two channel s, one sending task and one receiving task.
// Define channel var in chan int var done chan bool // 1. Sender in <- 1 in <- 2 <- done <- done // recipient var n int n <- in n <- in done <- true done <- true
In this way, Sleep() does not need to be added, because after the sender sends in, it also needs to receive the data sent by the receiver done (no actual reception), which leads to the need to wait for the receiver to send data, so that the task of the receiver in waiting for the task is over!
* With a theoretical basis, we can write small examples and put them into practice. ```go package main import ( "fmt" ) func doWork(id int, in chan int, done chan bool) { // range read in channel data for n := range in { // Processing in received data fmt.Printf("Worker %d received %c\n", id, n) // The feedback information has been processed done <- true } } type worker struct { in chan int done chan bool } // Create work func createWorker(id int) worker { w := worker { in: make(chan int), done: make(chan bool), } go doWork(id, w.in, w.done) return w } // example func chanDemo() { // Create ten workers var workers [10]worker for i := 0; i < 10; i++ { workers[i] = createWorker(i) } // Assign tasks to each worker and pass in a value for i, worker := range workers { worker.in <- 'A' + i } // Assign tasks to each worker and pass in a value for i, worker := range workers { worker.in <- 'a' + i } // wait for all oh them, wait for the return of all done, so that the sender can execute it concurrently // Because each doWork above receives values twice, it needs to receive values twice for _, worker := range workers { <- worker.done <- worker.done } } func main() { chanDemo() } // Writing like this will not execute correctly!!, The reason is that after a data is processed in dowork, it will be < - done, but it is not processed in chanDemo. Even if this is processed, dowork will always be stuck in < - done, waiting for others to receive it, but chanDome does not receive it, but sends a group of data to in < -. This leads to both sides waiting and deadlock. The solution can be 1. doWork Medium: go func() {done <- true} // Create a process again and execute this sentence. 2. Or: set cache! createWorker() In function: w := worker { in: make(chan int), done: make(chan bool), } You can set the cache when done <- true When, they are immediately waiting for others to collect w := worker { in: make(chan int), done: make(chan bool, 2), // Can cache } 3. Or the actual business will not have too complex concurrency. Here, you can separate the reception. Do not wait for the results of two batches of tasks at one time chanDome() Medium: // Assign tasks to each worker and pass in a value for i, worker := range workers { worker.in <- 'A' + i } // After sending a group, wait for the result immediately for _, worker := range workers { <- worker.done } // Assign tasks to each worker and pass in a value for i, worker := range workers { worker.in <- 'a' + i } // After sending a group, wait for the result immediately for _, worker := range workers { <- worker.done }
-
Wait using sync.WaitGroup
var wg sync.WaitGroup // Three methods are provided wg.Add() // Receiving int type: the number of tasks is used by the receiver wg.Done() // The end of task processing flag is used by the processor wg.Wait() // Waiting for all tasks to end is used by the processor
-
Remove the done channel of the above code and use WaitGroup instead
package main import ( "fmt" "sync" ) func doWork(id int, in chan int, wg *sync.WaitGroup) { // range read in channel data for n := range in { // Processing in received data fmt.Printf("Worker %d received %c\n", id, n) // The feedback information has been processed wg.Done() } } type worker struct { in chan int // Add WaitGroup wg *sync.WaitGroup // Use the pointer to call the same copy. It can't be alone }
//Create work
func createWorker(id int, wg *sync.WaitGroup) worker {
w := worker {
in: make(chan int),
wg: wg,
}
go doWork(id, w.in, w.wg)
return w
}//Examples
func chanDemo() {
//Create WaitGroup
var wg sync.WaitGroup
//Create ten workers
var workers [10]worker
for i := 0; i < 10; i++ {
workers[i] = createWorker(i, &wg)
}//We set a total of 20 tasks below
wg.Add(20)//Assign tasks to each worker and pass in a value
for i, worker := range workers {
worker.in <- 'A' + i
//In addition to the one-time Add(20), when the total number of tasks is not known, you can create an Add(1) here
}//Assign tasks to each worker and pass in a value
for i, worker := range workers {
worker.in <- 'a' + i
}//Wait
wg.Wait()
}func main() {
chanDemo()
}
3. channel traverses the tree and compares it with functional recursion
-
Upper code
package main import "fmt" type TreeNode struct { Left *TreeNode Right *TreeNode Val int } //1. Use functional programming to traverse the tree and middle order func (node *TreeNode) TraverseFunc(f func(*TreeNode)) { if node == nil { return } node.Left.TraverseFunc(f) f(node) node.Right.TraverseFunc(f) } // 2. Use channel to traverse the number // As in the previous example, TraverseWithChannel() is equivalent to createWorker. Create a channel and return func (node *TreeNode) TraverseWithChannel() chan *TreeNode { out := make(chan *TreeNode) // Open goroutine and write node go func() { node.TraverseFunc(func(node *TreeNode) { out <- node }) // close close(out) }() return out } func main() { // 0. Create the tree first root := TreeNode{nil, nil, 1} root.Left = &TreeNode{nil, nil, 5} root.Right = &TreeNode{Val: 4} root.Right.Left = new(TreeNode) root.Left.Right = &TreeNode{nil, nil, 3} // 1. Call functional traversal, f anonymous function, and count the number of nodes count := 0 root.TraverseFunc(func(node *TreeNode) { count++ fmt.Println(node.Val) }) fmt.Println("Tree has Node count: ", count) // 2. Through channel c:= root.TraverseWithChannel() // The nodes can be traversed through the range, and the largest element value can be counted maxNodeVal := 0 for node := range c { if node.Val > maxNodeVal { maxNodeVal = node.Val } } fmt.Println("Max Node Value: ", maxNodeVal) } // Here some are like yield in Python. Out < - node, then switch. After you get it in the range, execute the switch again. // The channel of go language can do more complex things, but there are some functions equivalent to yield
4. select to schedule
-
The example given by the teacher is quite complex. It also talked about the timer, which is a very useful scenario example. I just listened to the back.
package main import ( "fmt" "math/rand" "time" ) // Create channel func generator() chan int { out := make(chan int) i := 0 go func() { for { // Random sleep 1500 ms time.Sleep( time.Duration(rand.Intn(1500)) * time.Millisecond) out <- i i++ } }() return out }
func main() {
var c1, c2 = generator(), generator()
//If we want to receive data from c1 and c2, and who will receive it quickly, we need to use select
//n1 := <- c1
//n2 := <- c2//It's like doing a non blocking process ~ return the result immediately
for {
select {
case n := <- c1:
fmt.Println("Receive from c1: ", n)
case n := <- c2:
fmt.Println("Receive from c2: ", n)
//Default: / / if you do not add default, you will get blocked and wait for data
// fmt.Println("No value received")
}
}
} -
Summary:
- Use of Select
- Use of timer
- Using nil Channel in Select
5. Traditional synchronization mechanism
-
In the complex example mentioned above, no lock is used, and the data is shared through the channel. This is the CSP mechanism.
-
There are also traditional mechanisms, such as mutex, cond and waitgroup
-
Mutex (mutex)
package main import ( "fmt" "sync" "time" ) // Make the int type of the atomic operation. Note: the system has atomic operation. atomic.AddInt32(), etc. to ensure "thread safety", // go is to ensure security in multiple goroutine s executed concurrently type atomicInt struct { val int lock sync.Mutex } // Build two functions func (a *atomicInt) increment() { // Protect with a lock a.lock.Lock() defer a.lock.Unlock() a.val++ } func (a *atomicInt) get() int { // Lock unlock a.lock.Lock() defer a.lock.Unlock() return a.val } func main() { var a atomicInt a.increment() go func() { a.increment() }() time.Sleep(time.Millisecond) fmt.Println(a.get()) }
-
The traditional synchronization mechanism is rarely used in g language, and channel is used for communication as much as possible. Traditional synchronization mechanisms communicate through shared memory and need to be locked for protection.
Chapter 12: a maze game, algorithm example
-
There is a file:
// maze.in where the first line is the number of rows and columns of the file. The second line is separated by spaces. // 0 represents a road and 1 represents a wall. Randomly give the starting point (start) and the ending point (end). Mark path 6 5 0 1 0 0 0 0 0 0 1 0 0 1 0 1 0 1 1 1 0 0 0 1 0 0 1 0 1 0 0 0
-
code implementation
package main import ( "fmt" "os" ) // 1. Read and return a two-dimensional array from the file func readMaze(filename string) [][]int { // Open file file, err := os.Open(filename) if err != nil { panic(err) } // Read rows and columns of two-dimensional array var row, col int fmt.Fscanf(file, "%d %d", &row, &col) // To add the access address, the value transfer cannot change the external address maze := make([][]int, row) // Creating a two-dimensional array is actually a one-dimensional array, and the elements are one-dimensional arrays for i := range maze { maze[i] = make([]int, col) for j := range maze[i] { // Read index fmt.Fscanf(file, "%d", &maze[i][j]) } } return maze } // Defines the representation of coordinate points type point struct { i, j int } // Defines the direction of top left bottom right var dirs = [4]point { {-1, 0}, {0, -1}, {1, 0}, {0, 1}} // Method for realizing coordinate addition func (p point) add(r point) point{ // Returns a new value using the value type return point{p.i + r.i, p.j + r.j} } // Judge whether the point position is out of bounds func (p point) at(grid [][]int) (int, bool) { if p.i < 0 || p.i >= len(grid) { return 0, false } if p.j < 0 || p.j >= len(grid[p.i]) { return 0, false } return grid[p.i][p.j], true } func walk(maze [][]int, start, end point) [][]int{ // Establish coordinate route steps := make([][]int, len(maze)) for i := range steps { steps[i] = make([]int, len(maze[i])) } // Breadth first solution // 1. Establish a queue Q := []point {start} for len(Q) > 0 { // Get the team head element cur := Q[0] Q = Q[1:] // Gets the location of the element curSteps, _ := cur.at(steps) // At the end: if cur == end { break } // Go up, down, left and right for _, dir := range dirs { // Get new node location next := cur.add(dir) // Judge whether the position is legal, cross the boundary or hit the wall val, ok := next.at(maze) if !ok || val == 1 { continue } // Judge whether this point has passed val, ok = next.at(steps) if !ok || val != 0 { continue } // Not even equal to the origin if next == start {continue} // Let's start work, record the next element step number, and then add it to the Q queue steps[next.i][next.j] = curSteps + 1 Q = append(Q, next) } } return steps } func main() { // Get 2D slice maze := readMaze("maze/maze.in") // Print //for _, row := range maze { // for _, num := range row { // fmt.Printf("%d ", num) // } // fmt.Println() //} // Labyrinth steps := walk(maze, point{0, 0}, point{ 2,2}) // Print results for _, row := range steps { for _, num := range row { fmt.Printf("%4d", num) // %4d the last output occupies three bits } fmt.Println() } }
Chapter 13: Standard Library
http Library
-
As a network-oriented, service-oriented and highly concurrent programming language, go language encapsulates http very well.
-
Can act as a server
-
It can also be used as a client: crawler, request
- Send request using http client
- Use http.Client to control request headers, etc
- Simplify work with httputil
package main import ( "fmt" "net/http" "net/http/httputil" ) func main() { request, err := http.NewRequest(http.MethodGet, "http://www.imooc.com", nil) request.Header.Add("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1") // Custom Client client := http.Client{ CheckRedirect: func( req *http.Request, via []*http.Request) error { fmt.Println("Redirect: ", req) return nil }, } resp, err := client.Do(request) //resp, err := http.DefaultClient.Do(request) //resp, err := http.Get("http://www.imooc.com") if err != nil { panic(err) } defer resp.Body.Close() s, err := httputil.DumpResponse(resp, true) // One time analysis if err != nil { panic(err) } fmt.Printf("%s\n", s) }
-
Performance analysis of http server
- inport _ "net/http/pprof" is not in the program, but in the terminal. So add one_ Indicates use
- Visit / debug/pprof
- Use go tool pprof
2. Other standard libraries
-
bufio
-
log
-
encoding/json
-
regexp
-
time
- time.Sleep()
- Calculation time, etc
- Generate channel, timer, etc
-
strings/ math/ rand
-
How to view a standard library document
-
Start a server yourself
- godoc -http:8888 (I can't start it)
-
https://studygolang.com/pkgdoc
-
Create a queue: https://studygolang.com/pkgdoc
-
String to number, number to string: https://blog.csdn.net/weixin_34185560/article/details/91438265
-
String splicing (delete one of the characters)
st := "12345678" st = st[:2] + st[3:8] fmt.Println(st) // 1245678
//Splicing
rst := []string {"1", "2", "3"}
fmt.Println(strings.Join(rst, ","))// 1,2,3
- Some small summary
package main import "fmt" type a struct { b int } type w struct { s *a } func index(x int) { // 1. The defined parameters may not be used fmt.Println(123) } func main() { index(2) // Defining parameters may not be used aa := a{1} w := w{&aa} w.s.b = 3 // 2. The structure is also a value transfer, which needs to be passed by pointer to be the same copy fmt.Println(w.s) // Result &{3} fmt.Println(aa) // Result {3} }
[][]int{
//Establish coordinate route
steps := make([][]int, len(maze))
for i := range steps {
steps[i] = make([]int, len(maze[i]))
}
// Breadth first solution // 1. Establish a queue Q := []point {start} for len(Q) > 0 { // Get the team head element cur := Q[0] Q = Q[1:] // Gets the location of the element curSteps, _ := cur.at(steps) // At the end: if cur == end { break } // Go up, down, left and right for _, dir := range dirs { // Get new node location next := cur.add(dir) // Judge whether the position is legal, cross the boundary or hit the wall val, ok := next.at(maze) if !ok || val == 1 { continue } // Judge whether this point has passed val, ok = next.at(steps) if !ok || val != 0 { continue } // Not even equal to the origin if next == start {continue} // Let's start work, record the next element step number, and then add it to the Q queue steps[next.i][next.j] = curSteps + 1 Q = append(Q, next) } } return steps
}
func main() {
//Get 2D slice
maze := readMaze("maze/maze.in")
//Print
//for _, row := range maze {
// for _, num := range row {
// fmt.Printf("%d ", num)
// }
// fmt.Println()
//}
// Labyrinth steps := walk(maze, point{0, 0}, point{ 2,2}) // Print results for _, row := range steps { for _, num := range row { fmt.Printf("%4d", num) // %4d the last output occupies three bits } fmt.Println() }
}
## Chapter 13: Standard Library ### http Library * go Language as a network-oriented, service-oriented, high concurrency programming language http The packaging is very good. * Can act as a server * It can also be used as a client: crawler, request * use http client poke request * use http.Client Control request header, etc * use httputil Simplify work ```go package main import ( "fmt" "net/http" "net/http/httputil" ) func main() { request, err := http.NewRequest(http.MethodGet, "http://www.imooc.com", nil) request.Header.Add("User-Agent", "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1") // Custom Client client := http.Client{ CheckRedirect: func( req *http.Request, via []*http.Request) error { fmt.Println("Redirect: ", req) return nil }, } resp, err := client.Do(request) //resp, err := http.DefaultClient.Do(request) //resp, err := http.Get("http://www.imooc.com") if err != nil { panic(err) } defer resp.Body.Close() s, err := httputil.DumpResponse(resp, true) // One time analysis if err != nil { panic(err) } fmt.Printf("%s\n", s) }
-
Performance analysis of http server
- inport _ "net/http/pprof" is not in the program, but in the terminal. So add one_ Indicates use
- Visit / debug/pprof
- Use go tool pprof
2. Other standard libraries
-
bufio
-
log
-
encoding/json
-
regexp
-
time
- time.Sleep()
- Calculation time, etc
- Generate channel, timer, etc
-
strings/ math/ rand
-
How to view a standard library document
-
Start a server yourself
- godoc -http:8888 (I can't start it)
-
https://studygolang.com/pkgdoc
-
Create a queue: https://studygolang.com/pkgdoc
-
String to number, number to string: https://blog.csdn.net/weixin_34185560/article/details/91438265
-
String splicing (delete one of the characters)
st := "12345678" st = st[:2] + st[3:8] fmt.Println(st) // 1245678
//Splicing
rst := []string {"1", "2", "3"}
fmt.Println(strings.Join(rst, ","))// 1,2,3
- Some small summary
package main import "fmt" type a struct { b int } type w struct { s *a } func index(x int) { // 1. The defined parameters may not be used fmt.Println(123) } func main() { index(2) // Defining parameters may not be used aa := a{1} w := w{&aa} w.s.b = 3 // 2. The structure is also a value transfer, which needs to be passed by pointer to be the same copy fmt.Println(w.s) // Result &{3} fmt.Println(aa) // Result {3} }
- FMT. Printf ('% p') can print memory addresses.