作为一个BanG Dream! It’s MyGO!!!!!的忠实粉丝和Counter-Strike: Global Offensive的忠实玩家,Go语言自然也是必不可少的
本文记录了学习Go官网中的Go by Example 的笔记和一些从别的地方的东西
basic 先看一个例子
1 2 3 4 5 6 7 8 9 10 11 12 13 package mainimport ( "fmt" "math" ) func main () { fmt.Println("go" + "lang" ) fmt.Println("1+1 =" , 1 +1 ) fmt.Println("7.0/3.0 =" , 7.0 /3.0 ) fmt.Println(true && false ) fmt.Println(true || false ) fmt.Println(!true ) }
看起来第一行代表使用 package main
声明这个文件属于 main 包
fmt
代表输入输出的包
run 使用
来编译运行
var 可以使用var 变量名 类型,如
声明一个
仅在函数内部可以使用:=
,如
可以类型推断,也能指定
1 2 var a int = 10 var b = 20
以及用来枚举的iota //todo
1 2 3 4 5 const ( Sunday = iota Monday Tuesday )
string 首先, 在fmt中有String()方法,可以用来输出string
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 package mainimport ( "fmt" ) type coordinate struct { d, m, s float64 h rune } func (c coordinate) String() string { return fmt.Sprintf("%g° %g'%g\" %c" , c.d, c.m, c.s, c.h) } func main () { lat := coordinate{d: 4 , m: 30 , s: 0.0 , h: 'N' } long := coordinate{d: 135 , m: 54 , s: 0.0 , h: 'E' } fmt.Printf("Elysium Planitia is at %s, %s\n" , lat, long) }
还可以导入strings包来进行string的操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package mainimport ( "fmt" s "strings" ) var p = fmt.Printlnfunc main () { p("Contains: " , s.Contains("test" , "es" )) p("Count: " , s.Count("test" , "t" )) p("HasPrefix: " , s.HasPrefix("test" , "te" )) p("HasSuffix: " , s.HasSuffix("test" , "st" )) p("Index: " , s.Index("test" , "e" )) p("Join: " , s.Join([]string {"a" , "b" }, "-" )) p("Repeat: " , s.Repeat("a" , 5 )) p("Replace: " , s.Replace("foo" , "o" , "0" , -1 )) p("Replace: " , s.Replace("foo" , "o" , "0" , 1 )) p("Split: " , s.Split("a-b-c-d-e" , "-" )) p("ToLower: " , s.ToLower("TEST" )) p("ToUpper: " , s.ToUpper("test" )) }
输出为
1 2 3 4 5 6 7 8 9 10 11 12 13 $ go run string-functions.go Contains: true Count: 2 HasPrefix: true HasSuffix: true Index: 1 Join: a-b Repeat: aaaaa Replace: f00 Replace: f0o Split: [a b c d e] ToLower: test ToUpper: TEST
For if/else switch for有许多写法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package mainimport "fmt" func main () { i := 1 for i <= 3 { fmt.Println(i) i = i + 1 } for j := 0 ; j < 3 ; j++ { fmt.Println(j) } for i := range 3 { fmt.Println("range" , i) } for { fmt.Println("loop" ) break } if num := 9 ; num < 0 { fmt.Println(num, "is negative" ) } else if num < 10 { fmt.Println(num, "has 1 digit" ) } else { fmt.Println(num, "has multiple digits" ) } jk := 2 fmt.Print("Write " , jk, " as " ) switch jk { case 1 : fmt.Println("one" ) case 2 : fmt.Println("two" ) case 3 : fmt.Println("three" ) } }
反正就和c语言把小括号去了差不多
Arrays 1 2 3 4 5 6 7 var a [5 ]int b = [...]int {100 , 3 : 400 , 500 } twoD = [2 ][3 ]int { {1 , 2 , 3 }, {1 , 2 , 3 }, }
Slices:类似于可变长数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 c := make ([]string , len (s)) copy (c, s) l = s[:5 ] fmt.Println("sl2:" , l) l = s[2 :] fmt.Println("sl3:" , l) twoD := make ([][]int , 3 ) for i := range twoD { twoD[i] = make ([]int , 4 ) }
maps 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 func main () { set := make (map [string ]bool ) set["apple" ] = true set["banana" ] = true if set["apple" ] { fmt.Println("Apple is in the set" ) } delete (set, "banana" ) for item := range set { fmt.Println(item) } } func main () { users := map [string ]map [string ]string { "alice" : { "email" : "alice@example.com" , "phone" : "123-456-7890" , }, "bob" : { "email" : "bob@example.com" , "phone" : "098-765-4321" , }, } fmt.Println("Alice's email:" , users["alice" ]["email" ]) users["charlie" ] = map [string ]string { "email" : "charlie@example.com" , "phone" : "111-222-3333" , } for name, info := range users { fmt.Printf("%s: Email - %s, Phone - %s\n" , name, info["email" ], info["phone" ]) } }
Functions 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package mainimport "fmt" func plus (a int , b int ) int { return a + b } func plusPlus (a, b, c int ) int { return a + b + c } func main () { res := plus(1 , 2 ) fmt.Println("1+2 =" , res) res = plusPlus(1 , 2 , 3 ) fmt.Println("1+2+3 =" , res) }
Variadic Functions 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package mainimport "fmt" func sum (nums ...int ) { fmt.Print(nums, " " ) total := 0 for _, num := range nums { total += num } fmt.Println(total) } func main () { nums := []int {1 , 2 , 3 , 4 } sum(nums...) }
Closure 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package mainimport "fmt" func intSeq () func () int { i := 0 return func () int { i++ return i } } func main () { nextInt := intSeq() fmt.Println(nextInt()) fmt.Println(nextInt()) fmt.Println(nextInt()) newInts := intSeq() fmt.Println(newInts()) }
oop struct 和c的差不多
method 感觉这个写法有点神人
就是与特定类型关联的函数,比如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package mainimport "fmt" type rect struct { width, height int } func (r *rect) area() int { return r.width * r.height } func (r rect) perim() int { return 2 *r.width + 2 *r.height } func main () { r := rect{width: 10 , height: 5 } fmt.Println("area: " , r.area()) fmt.Println("perim:" , r.perim()) rp := &r fmt.Println("area: " , rp.area()) fmt.Println("perim:" , rp.perim()) }
还是要注意指针和值
会在改变rp的时候改变r的值
interface 约定俗成:一般接口名字结尾用er
和别的语言也差不多,这就是go中的多态的实现
关于其他:没有class,对象,继承,面向对象就用method和interface的组合来实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package mainimport ( "fmt" "math" ) type geometry interface { area() float64 perim() float64 } type rect struct { width, height float64 } type circle struct { radius float64 } func (r rect) area() float64 { return r.width * r.height } func (r rect) perim() float64 { return 2 *r.width + 2 *r.height } func (c circle) area() float64 { return math.Pi * c.radius * c.radius } func (c circle) perim() float64 { return 2 * math.Pi * c.radius } func measure (g geometry) { fmt.Println(g) fmt.Println(g.area()) fmt.Println(g.perim()) } func main () { r := rect{width: 3 , height: 4 } c := circle{radius: 5 } measure(r) measure(c) }
struct 嵌入实现转发 在struct中只给定字段类型,不给定字段名即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package mainimport ( "fmt" ) type report struct { sol int temperture location } type temperture struct { high, low celsius } type location struct { lat, long float64 } type celsius float64 func (t temperture) average() celsius { return (t.high + t.low) / 2 } func main () { bradbury := location{-4.5895 , 137.4417 } t := temperture{high: -1.0 , low: -78.0 } report := report{ sol: 15 , temperture: t, location: bradbury, } fmt.Println(report.average()) }
error nil 声明为接口的变量未被赋值的时候零值是nil
接口类型的变量只有在类型和值都是nil的时候才为nil
例题:
• 亚瑟被一位骑士挡住了去路。正如 leftHand item 变量的值 nil 所示,这位英雄手上正空无一物。 请实现一个拥有 pickup(i item) 和 give(to *character) 等方法的character 结构,然后使用你在本节学到的知识编写一个脚本,使得亚瑟可以拿起一件物品并将其交给骑士,与此同时为每个动作打印出适当的描述。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package mainimport ( "fmt" ) type item struct { name string } type character struct { name string leftHand *item } func (c *character) pickup(i *item) { if c.leftHand != nil { fmt.Printf("%s's hand is already occupied.\n" , c.name) return } c.leftHand = i fmt.Printf("%s picks up %s.\n" , c.name, i.name) } func (c *character) give(to *character) { if c.leftHand == nil { fmt.Printf("%s has nothing to give.\n" , c.name) return } if to.leftHand != nil { fmt.Printf("%s cannot accept %s; hand is occupied.\n" , to.name, c.leftHand.name) return } fmt.Printf("%s gives %s to %s.\n" , c.name, c.leftHand.name, to.name) to.leftHand = c.leftHand c.leftHand = nil } func main () { arthur := &character{name: "Arthur" } knight := &character{name: "The Knight" } sword := &item{name: "Excalibur" } arthur.pickup(sword) arthur.give(knight) }
error 按照惯例函数返回最后一个值应该是表示错误
可以用New来创建一个创建一个新的、简单的error
1 var ErrDivideByZero = errors.New("cannot divide by zero")
这样后面就可以用
1 2 3 4 5 6 7 8 if err == ErrDivideByZero { fmt.Println("Error:", err) } else { fmt.Println("Unexpected error:", err) } } else { fmt.Println("Result:", result) }
自定义错误类型:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package mainimport ( "errors" "fmt" ) type argError struct { arg int message string } func (e *argError) Error() string { return fmt.Sprintf("%d - %s" , e.arg, e.message) } func f (arg int ) (int , error ) { if arg == 42 { return -1 , &argError{arg, "can't work with it" } } return arg + 3 , nil } func main () { _, err := f(42 ) var ae *argError if errors.As(err, &ae) { fmt.Println(ae.arg) fmt.Println(ae.message) } else { fmt.Println("err doesn't match argError" ) } }
defer 允许推迟函数的执行直到当前函数返回。这在处理资源清理、锁的释放、文件关闭等场景中特别有用
defer
语句的执行时机在函数返回之前,即使它位于return
语句之后:
多个 defer
语句,它们会以后进先出(LIFO)的顺序执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package mainimport "fmt" func main () { fmt.Println("Enter main" ) defer fmt.Println("Defer in main - 1" ) func () { fmt.Println("Enter anonymous function" ) defer fmt.Println("Defer in anonymous function - 1" ) defer fmt.Println("Defer in anonymous function - 2" ) fmt.Println("Exit anonymous function" ) }() deferOrder() defer fmt.Println("Defer in main - 2" ) fmt.Println("Exit main" ) } func deferOrder () { defer fmt.Println("Defer in deferOrder - 1" ) defer fmt.Println("Defer in deferOrder - 2" ) fmt.Println("In deferOrder function" ) }
输出为
1 2 3 4 5 6 7 8 9 10 11 Enter main Enter anonymous function Exit anonymous function Defer in anonymous function - 2 Defer in anonymous function - 1 In deferOrder function Defer in deferOrder - 2 Defer in deferOrder - 1 Exit main Defer in main - 2 Defer in main - 1
recover:用来处理panic 1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport "fmt" func mayPanic () { panic ("a problem" ) } func main () { defer func () { if r := recover (); r != nil { fmt.Println("Recovered. Error:\n" , r) } }() mayPanic() fmt.Println("After mayPanic()" ) }
输出为
1 2 3 $ go run recover.go Recovered. Error: a problem
concurrent goroutine Goroutine是Go语言中最基本的并发执行单元。它是一个轻量级的”线程”,由Go运行时管理
创建一个goroutine非常简单,只需要在函数调用前加上go
关键字:
1 2 3 4 5 6 7 8 func hello () { fmt.Println("Hello from goroutine!" ) } func main () { go hello() time.Sleep(1 * time.Second) }
Channel Channel是goroutine之间通信的管道。它可以让一个goroutine发送特定值到另一个goroutine
基本用法
1 2 3 4 5 6 7 8 9 10 11 12 func main() { // 创建一个整数类型的通道 ch := make(chan int) // 在goroutine中发送数据 go func() { ch <- 42 // 发送数据到通道 }() value := <-ch // 从通道接收数据 fmt.Println(value) }
Channel的类型:
无缓冲通道:
有缓冲通道:
同步机制 Go提供了多种同步机制:
WaitGroup 用于等待一组goroutine完成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 var wg sync.WaitGroup func worker (id int ) { defer wg.Done() fmt.Printf("Worker %d starting\n" , id) time.Sleep(time.Second) fmt.Printf("Worker %d done\n" , id) } func main () { for i := 1 ; i <= 3 ; i++ { wg.Add(1 ) go worker(i) } wg.Wait() }
Mutex(互斥锁) 用于保护共享资源:
1 2 3 4 5 6 7 8 9 10 var ( mutex sync.Mutex counter int ) func increment () { mutex.Lock() counter++ mutex.Unlock() }
Select Select语句可以同时等待多个通道操作:
1 2 3 4 5 6 7 8 select { case msg1 := <-ch1: fmt.Println("Received from ch1:" , msg1) case msg2 := <-ch2: fmt.Println("Received from ch2:" , msg2) case <-time.After(time.Second): fmt.Println("Timeout" ) }