• Golang笔记


    0.

    https://www.cnblogs.com/WayneZeng/p/7598032.html

    环境变量

    • GOROOT:
      安装目录。包括自带的源码、标准库包。

    • GOPATH:
      工作目录, 允许多个目录。包含第三方源码。
      go install, go get, go run等命令会用到GOPATH环境变量。
      当存在多个路径时, 优先采用第一个路径

      GOPATH之下主要包含三个目录:
      bin: 存放可执行程序;
      pkg: 存放编译好的中间文件, 主要是*.a文件
      src: 存放go的源文件, 按包的不同进行组织

    • GOBIN:
      go install编译存放路径, 可以为空.
      为空时则遵循“约定优于配置”原则, 可执行文件放在各自GOPATH目录的bin文件夹中(前提: package main的main函数文件不能直接放到GOPATH的src下面)

    开发命令相关

    • 整体开发目录
    go_project     // go_project为GOPATH目录
        -- bin
            -- myApp1  // 编译生成
            -- myApp2  // 编译生成
        -- pkg
        -- src
            -- myApp1     // project1
                -- models
                -- controllers
                -- others
                -- main.go 
            -- myApp2     // project2
                -- models
                -- controllers
                -- others
                -- main.go 
    
    • import:
    1. 导入标准库搜索路径:
      $GOROOT/src
    2. 导入私人库搜索路径:
      $GOPATH/src

    import后接包的相对路径(相对$GOROOT/src和$GOPATH/src), 通常相对路径的文件夹名与包名相同。

    • go get

      参数 [main.go所在路径]:可选。相对【$GOPATH/src】路径。缺省是.(src自己)。可指定src下面的子文件夹路径。

      1. git clone到【$GOPATH/src】 (缺省)
      2. 执行go install
    • go build
      编译, 在当前目录下生成可执行文件, 文件名默认与当前目录名同名, win下为xxx.exe

    • go install

      参数 [main.go所在路径]:可选。相对【$GOPATH/src】路径。缺省是.(src自己)。可指定src下面的子文件夹路径。

      1. 编译源代码, 若是可执行文件 (package "main"且包含main方法), 则会编译生成可执行文件到【$GOBIN】目录下, $GOBIN为空时, 为【$GOPATH/bin】目录下;
      2. 可执行文件import引入的其他包, 就会被编译到【$GOPATH/pkg/$GOOS_$GOARCH】目录下;
      3. 若是一个普通的包,会被编译生成到pkg目录下, 生成文件的文件名以.a结尾

    builtin

    Constants
    Variables
    func append(slice []Type, elems ...Type) []Type
    func cap(v Type) int
    func close(c chan<- Type)
    func complex(r, i FloatType) ComplexType
    func copy(dst, src []Type) int
    func delete(m map[Type]Type1, key Type)
    func imag(c ComplexType) FloatType
    func len(v Type) int
    func make(t Type, size ...IntegerType) Type
    func new(Type) *Type
    func panic(v interface{})
    func print(args ...Type)
    func println(args ...Type)
    func real(c ComplexType) FloatType
    func recover() interface{}
    
    type ComplexType
    type FloatType
    type IntegerType
    type Type
    type Type1
    type bool
    type byte
    type complex128
    type complex64
    type error
    type float32
    type float64
    type int
    type int16
    type int32
    type int64
    type int8
    type rune
    type string
    type uint
    type uint16
    type uint32
    type uint64
    type uint8
    type uintptr
    

    1. 类型定义

    package main
    type Node struct {
        X, Y int
    }
    var (
        x int = 3
        y string = "hi"
        px *int = &x
        v1 = Vertex{1, 2}  // 类型为 Vertex
        v2 = Vertex{X: 1}  // Y:0 被省略
        v3 = Vertex{}      // X:0 和 Y:0
        p  = &Vertex{1, 2} // 类型为 *Vertex
        a [2]string        // string数组
    )
    func main() {
        var c, python, java bool
        // var c, python, java = true, false, "no!"
        // c, python, java := true, false, "no!"
        // 在函数中, :=简洁赋值语句在明确类型的地方, 可以用于替代 var 定义。
        // 函数外的每个语句都必须以关键字开始(var、 func等), :=结构不能使用在函数外
    }
    func add(x, y int) int {
        return x + y
    }
    

    2. slice

    • 一个 slice 会指向一个序列的值,并且包含了长度信息。
    • []T是一个元素类型为 T的 slice。
    • len(s)返回 slice s 的长度。

    slice的slice

    • slice 可以重新切片,创建一个新的 slice 值指向相同的数组。
    • s[lo:hi]表示从 lo到 hi-1的 slice 元素,含左端,不包含右端
    • s[lo:hi][lo2:hi2]表示在切片一次的基础上再切片一次

    make: 构造slice

    • slice 由函数make创建。这会分配一个全是零值的数组并且返回一个 slice 指向这个数组:
    • a := make([]int, 5) // len(a)=5
    • 为了指定容量,可传递第三个参数到 make:
        b := make([]int, 0, 5) // len(b)=0, cap(b)=5
        b = b[:cap(b)] // len(b)=5, cap(b)=5
        b = b[1:] // len(b)=4, cap(b)=4
    
    • slice 的零值是 nil , 其长度和容量是 0。

    append: 向slice添加元素

    • func append(s []T, vs ...T) []T
    • append的第一个参数 s是一个元素类型为 T的 slice ,其余类型为 T的值将会附加到该 slice 的末尾。
    • append的结果是一个包含原 slice 所有元素加上新添加的元素的 slice。
    • 如果s的底层数组太小,而不能容纳所有值时,会分配一个更大的数组。 返回的 slice 会指向这个新分配的数组。

    range

    • for循环的 range格式可以对 slice 或者 map 进行迭代循环。
    • 当使用 for循环遍历一个 slice 时,每次迭代 range将返回两个值。 第一个是当前下标(序号),第二个是该下标所对应元素的一个拷贝。

    map

    • map 映射键到值。
    • map 在使用之前必须用 make来创建;值为 nil的 map 是空的,并且不能对其赋值。
    package main
    
    import "fmt"
    
    type Vertex struct {
        Lat, Long float64
    }
    
    var m map[string]Vertex
    
    func main() {
        m = make(map[string]Vertex)
        m["Bell Labs"] = Vertex{
            40.68433, -74.39967,
        }
        fmt.Println(m["Bell Labs"])
    }
    

    或直接初始化

    package main
    
    import "fmt"
    
    type Vertex struct {
        Lat, Long float64
    }
    
    var m = map[string]Vertex{
        "Bell Labs": {40.68433,-74.39967},
        "Google": {37.42202, -122.08408},
    }
    
    func main() {
        fmt.Println(m)
    }
    
    • 在 map m中插入或修改一个元素:
      m[key] = elem
    • 获得元素:
      elem = m[key]
    • 删除元素:
      delete(m, key)
    • 通过双赋值检测某个键存在:
      elem, ok = m[key] // elem为nil值时, ok = false

    闭包

    package main
    
    import "fmt"
    
    func adder() func(int) int {
        sum := 0
        return func(x int) int {
            sum += x
            return sum
        }
    }
    
    func main() {
        pos, neg := adder(), adder()
        for i := 0; i < 10; i++ {
            fmt.Println(
                pos(i),
                neg(-2*i),
            )
        }
    }
    
    package main
    
    import "fmt"
    
    // 返回一个“返回int的函数”
    func fibonacci() func() int {
    	a,b := 1, 0
    	return func() int {
    		a, b = b, a+b
    		return a
    	}
    }
    
    func main() {
    	f := fibonacci()
    	for i := 0; i < 10; i++ {
    		fmt.Println(f())
    	}
    }
    
    

    类型断言

    如果T是具体类型

    类型断言检查x的动态类型是否等于具体类型T。如果检查成功,类型断言返回的结果是x的动态值,其类型是T,否则panic。

    var w io.Writer
    w = os.Stdout
    f := w.(*os.File) // success: f == os.Stdout
    c := w.(*bytes.Buffer) // panic: interface holds *os.File, not *bytes.Buffer
    

    如果T是接口类型

    类型断言检查x的动态类型是否满足T。如果检查成功,返回值是一个类型为T的接口值。换句话说,到接口类型的类型断言,改变了表达式的类型,改变了(通常是扩大了)可以访问的方法,且保护了接口值内部的动态类型和值。

    var w io.Writer
    w = os.Stdout
    rw := w.(io.ReadWriter) // success: *os.File has both Read and Write
    w = new(ByteCounter)
    rw = w.(io.ReadWriter) // panic: *ByteCounter has no Read method
    

    无论T是什么类型,如果x是nil接口值,则类型断言失败。

    如果我们想知道类型断言是否失败,而不是失败时触发panic,可以使用返回两个值的版本

    y, ok := x.(T)

    Type Switches

    switch x.(type){
    	case nil: // 如果x是nil
    	case int, uint: 
    	case bool:
    	case string;
    	default: //没有匹配上
    }
    

    函数

    带指针参数的函数必须接受一个指针

    panic

    当panic异常发生时,程序会中断运行,并立即执行在该goroutine中被延迟的函数(defer 机制)。随后,程序崩溃并输出日志信息。

    recover

    如果在deferred函数中调用了内置函数recover,并且定义该defer语句的函数发生了panic异常,recover会使程序从panic中恢复,并返回panic value。导致panic异常的函数不会继续运行,但能正常返回。在未发生panic时调用recover,recover会返回nil。

    reflect

    • 反射可以大大提高程序的灵活性,使得interface{}有更大的发挥余地

      • 反射必须结合interface才玩得转
      • 变量的type要是concrete type的(也就是interface变量)才有反射一说
    • 反射可以将“接口类型变量”转换为“反射类型对象”

      • 反射使用 TypeOf 和 ValueOf 函数从接口中获取目标对象信息
    • 反射可以将“反射类型对象”转换为“接口类型变量

      • reflect.value.Interface().(已知的类型)
      • 遍历reflect.Type的Field获取其Field
    • 反射可以修改反射类型对象,但是其值必须是“addressable”

      • 想要利用反射修改对象状态,前提是 interface.data 是 settable,即 pointer-interface
    • 通过反射可以“动态”调用方法

    • 因为Golang本身不支持模板,因此在以往需要使用模板的场景下往往就需要使用反射(reflect)来实现

    方法

    不能对pointer、interface定义方法

    方法的接受者可以是 *T 或者 T。

    接收者为 *T 时,相当于传引用,传入可以为 *T 或 T(但不能是临时变量)。

    接受者为 T 时,相当于传值,传入可以为 *T 或 T。

    接口

    一个类型如果拥有一个接口需要的所有方法,这个类型就实现了这个接口。

    interface{}被称为空接口类型,我们可以将任意一个值赋给空接口类型。

    接口值,由两个部分组成,一个具体的类型type(某个结构体或者基本类型)和那个类型的值value。

    接口内的方法调用的时候,相当于value值为参数进行调用

    nil接口:类型和值都是nil。

    sort

    package sort
    type Interface interface {
    	Len() int
    	Less(i, j int) bool // i, j are indices of sequence elements
    	Swap(i, j int)
    }
    
    package sort
    type reverse struct{ Interface } // that is, sort.Interface
    func (r reverse) Less(i, j int) bool { return r.Interface.Less(j, i) }
    func Reverse(data Interface) Interface { return reverse{data} }
    

    常用

    slice

    make([][]int, 5)

    []int{0, 1, 2}

    map

    make(map[string][int])

    ma["hh"] = 2

    delete(ma, "hh")

    make/append/len/cap/range

    bulitin function

    func append(slice []Type, elems ...Type) []Type

    func copy(dst, src []Type) int

    func make(t Type, size ...IntegerType) Type

    func new(Type) *Type

    不定参

    package main
    
    import "fmt"
    
    func Sum(a ...int) int {
    	s:=0
    	for _,i:=range a{
    		s+=i
    	}
    	return s
    }
    func main()  {
    	fmt.Println(Sum(1,2,3,4))
    }
    

    协程

    package main
    
    import (
    	"fmt"
    	"time"
    )
    
    func say(s string) {
    	for i := 0; i < 5; i++ {
    		time.Sleep(100 * time.Millisecond)
    		fmt.Println(s)
    	}
    }
    
    func main() {
    	go say("world")
    	say("hello")
    }
    

    信道

    循环 for i := range c 会不断从信道接收值,直到它被关闭。

    package main
    
    import "fmt"
    
    func sum(s []int, c chan int) {
    	sum := 0
    	for _, v := range s {
    		sum += v
    	}
    	c <- sum // 将和送入 c
    }
    
    func main() {
    	s := []int{7, 2, 8, -9, 4, 0}
    
    	c := make(chan int)
    	go sum(s[:len(s)/2], c)
    	go sum(s[len(s)/2:], c)
    	x, y := <-c, <-c // 从 c 中接收
    
    	fmt.Println(x, y, x+y)
    }
    
    // 等价二叉树
    package main
    
    import "golang.org/x/tour/tree"
    import "fmt"
    // Walk 步进 tree t 将所有的值从 tree 发送到 channel ch。
    func dfs(t *tree.Tree, ch chan int) {
    	if t == nil {
    		return 
    	}
    	//ch <- t.Value
    	dfs(t.Left, ch)
    	ch <- t.Value
    	dfs(t.Right, ch)
    }
    
    func Walk(t *tree.Tree, ch chan int) {
    	dfs(t, ch)
    	close(ch)
    }
    
    // Same 检测树 t1 和 t2 是否含有相同的值。
    func Same(t1, t2 *tree.Tree) bool {
    	ch1, ch2 := make(chan int), make(chan int)
    	go Walk(t1, ch1)
    	go Walk(t2, ch2) 
    	for i := range ch1 {
    		if i != <-ch2 {
    			return false
    		}
    	}
    	_, ok := <-ch2
    	return !ok
    }
    
    func main() {
    	ch := make(chan int)
    	go Walk(tree.New(1), ch)
    	for num := range ch {
    		fmt.Println(num)
    	}
    	fmt.Println(Same(tree.New(1), tree.New(1)))
    	fmt.Println(Same(tree.New(1), tree.New(2)))
    }
    

    互斥锁

    package main
    
    import (
    	"fmt"
    	"sync"
    	"time"
    )
    
    // SafeCounter 的并发使用是安全的。
    type SafeCounter struct {
    	v   map[string]int
    	mux sync.Mutex
    }
    
    // Inc 增加给定 key 的计数器的值。
    func (c *SafeCounter) Inc(key string) {
    	c.mux.Lock()
    	// Lock 之后同一时刻只有一个 goroutine 能访问 c.v
    	c.v[key]++
    	c.mux.Unlock()
    }
    
    // Value 返回给定 key 的计数器的当前值。
    func (c *SafeCounter) Value(key string) int {
    	c.mux.Lock()
    	// Lock 之后同一时刻只有一个 goroutine 能访问 c.v
    	defer c.mux.Unlock()
    	return c.v[key]
    }
    
    func main() {
    	c := SafeCounter{v: make(map[string]int)}
    	for i := 0; i < 1000; i++ {
    		go c.Inc("somekey")
    	}
    
    	time.Sleep(time.Second)
    	fmt.Println(c.Value("somekey"))
    }
    

    并行爬虫

    package main
    
    import (
    	"fmt"
    )
    
    type Fetcher interface {
    	// Fetch 返回 URL 的 body 内容,并且将在这个页面上找到的 URL 放到一个 slice 中。
    	Fetch(url string) (body string, urls []string, err error)
    }
    
    // Crawl 使用 fetcher 从某个 URL 开始递归的爬取页面,直到达到最大深度。
    func Crawl(url string, depth int, fetcher Fetcher) {
    	// TODO: 并行的抓取 URL。
    	// TODO: 不重复抓取页面。
            // 下面并没有实现上面两种情况:
    	if depth <= 0 {
    		return
    	}
    	body, urls, err := fetcher.Fetch(url)
    	if err != nil {
    		fmt.Println(err)
    		return
    	}
    	fmt.Printf("found: %s %q
    ", url, body)
    	for _, u := range urls {
    		Crawl(u, depth-1, fetcher)
    	}
    	return
    }
    
    func main() {
    	Crawl("https://golang.org/", 4, fetcher)
    }
    
    // fakeFetcher 是返回若干结果的 Fetcher。
    type fakeFetcher map[string]*fakeResult
    
    type fakeResult struct {
    	body string
    	urls []string
    }
    
    func (f fakeFetcher) Fetch(url string) (string, []string, error) {
    	if res, ok := f[url]; ok {
    		return res.body, res.urls, nil
    	}
    	return "", nil, fmt.Errorf("not found: %s", url)
    }
    
    // fetcher 是填充后的 fakeFetcher。
    var fetcher = fakeFetcher{
    	"https://golang.org/": &fakeResult{
    		"The Go Programming Language",
    		[]string{
    			"https://golang.org/pkg/",
    			"https://golang.org/cmd/",
    		},
    	},
    	"https://golang.org/pkg/": &fakeResult{
    		"Packages",
    		[]string{
    			"https://golang.org/",
    			"https://golang.org/cmd/",
    			"https://golang.org/pkg/fmt/",
    			"https://golang.org/pkg/os/",
    		},
    	},
    	"https://golang.org/pkg/fmt/": &fakeResult{
    		"Package fmt",
    		[]string{
    			"https://golang.org/",
    			"https://golang.org/pkg/",
    		},
    	},
    	"https://golang.org/pkg/os/": &fakeResult{
    		"Package os",
    		[]string{
    			"https://golang.org/",
    			"https://golang.org/pkg/",
    		},
    	},
    }
    
  • 相关阅读:
    一个简单的爬虫case2
    一个简单的爬虫case1
    Kick Start 2018-Round H-Problem C. Let Me Count The Ways
    Kick Start 2018-Round H-Problem B. Mural
    Kick Start 2018-Round H-Problem A. Big Buttons
    211. Add and Search Word
    HDU-1506 Largest Rectangle in a Histogram
    HDU-1236 排名
    HDU-1009 FatMouse' Trade
    HDU-1231 最大连续子序列
  • 原文地址:https://www.cnblogs.com/dirge/p/11116552.html
Copyright © 2020-2023  润新知