Golang

变量定义

  1. 变量类型写在变量名之后

  2. 编译器可推测变量类型

  3. 没有char ,只有rune

  4. 原生支持复数类型

`

package main

import (
    "fmt"
    "math"
)

var ( //在外部定义是包内变量,并不是全局变量,所有函数都能用。在外部不能用:来初始化值
    aa = 12
    bb = "xc"
)

var cc = true //变量可都放在var的括号内

func variableZeroValue() { //初始化会赋初值
    var a int
    var s string
    fmt.Println(a,s) //0
    fmt.Printf("%d %s\n",a, s) //0
    fmt.Printf("%d %q\n", a, s) //0 ""


}

func variableInitialValue() {
    var a, b int = 3, 4 //多个数赋初值
    var s string = "abc"
    fmt.Println(a, b, s) //3 4 abc
}

func variableTypeDeduction() {
    var a, b, c, s = 3, 4, true, "abd" //能自动判断类型,从而进行赋值
    fmt.Println(a, b, c, s) //3 4 true abd
}

func variableShorter() {
    a, b, c, s := 3, 4, true, "asdf" //:和功能上面一样的,初始化时候有用
    fmt.Println(a, b, c, s) //3 4 true asdf
}

func triangle() { //类型需要强制转换,不能隐式转换
    var a, b int = 3, 4
    var c int
    c = int(math.Sqrt(float64(a*a+b*b)))
    fmt.Println(c) //5
}

func consts() { //常量,可作为任何数值使用,不用强制转换,必须赋值
    const filename = "name.vs"
    const a, b = 3, 4
    var c int
    c = int(math.Sqrt(a*a+b*b))
    fmt.Println(c) //5
}

func enums() { //枚举类型,可用const定义,iota是自增
    const(
        java = iota
        python
        golang
        javascript
    )
    fmt.Println(java, python, golang, javascript) //0 1 2 3

    const(
        k = 1 << (10*iota)
        kb
        mb
        gb
    )

    fmt.Println(k, kb,mb, gb) //1 1024 1048576 1073741824
}

func main() {
    fmt.Println("hello world") //hello world
    variableZeroValue()
    variableInitialValue()
    variableTypeDeduction()
    variableShorter()
    fmt.Println(aa,bb,cc) //12 xc true
    triangle()
    consts()
    enums()
}

`

条件语句if

  1. if和else if后面没有括号

`

package main

import (
    "io/ioutil"
    "fmt"
)

func main() {
    const filename  = "a.txt"
    content, err := ioutil.ReadFile(filename)
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Printf("%s\n",content)
    }
    //上下两个是等价的,if条件里可以赋值,下面这个con和error的变量作用域在if里
    if con, error := ioutil.ReadFile(filename);err != nil {
        fmt.Println(error)
    } else {
        fmt.Printf("%s\n",con)
    }

}

`

循环语句for

  1. for后面条件没有括号

`

package main

import (
    "fmt"
    "strconv"
    "os"
    "bufio"
)

func convertToBin(n int) string {
    result := ""
    for ; n > 0; n /= 2 { //可以省略初始条件
        lsb := n%2
        result = strconv.Itoa(lsb) + result;
    }
    return result
}

func printFile(filename string) {
    file, err := os.Open(filename)
    if err != nil {
        panic(err)     //报错处理
    }

    scanner := bufio.NewScanner(file)

    for scanner.Scan() {//可以省略初始条件和递增条件
        fmt.Println(scanner.Text())
    }
}

func forever()  {
    for { //死循环
        fmt.Println("ads")
    }
}

func main() {
    const filename  = "a.txt"

    fmt.Println(
        convertToBin(13), //1101

    )

    printFile("a.txt")

    forever()
}

`

函数

  1. 返回值类型写在最后面

  2. 可返回多个值

  3. 函数作为参数

  4. 没有默认参数,可选参数

`

package main

import (
    "fmt"
    "math"
)

func div(a, b int) (int, int) { //能返回多个参数,相同类型的参数,可公用一个类型
    return a/b, a%b
}

func apply(op func(int, int) int, a, b int) int { //参数为匿名函数
    return op(a, b)
}

func sum(numbers ...int) int { //可变参数列表
    s := 0
    for i := range numbers {
        s += numbers[i]
    }
    return s
}

func main() {
    fmt.Println(
        div(13,4),    //3 1
    )

    fmt.Println(
        apply(func(a int, b int) int {
            return int(math.Pow(float64(a),float64(b)))
        },3,4),
    ) //81

    fmt.Println(sum(1,2,3,4,5,6,7,8,9,10)) //55
}

`

指针

  1. 指针不能运算

`

package main

import "fmt"

func swap1(a, b *int) {
    *a,*b = *b,*a
}

func swap2(a, b int)(int, int)  {
    return b, a
}

func main() {
    a, b, c, d:= 1, 2, 1,2
    swap1(&a, &b)
    fmt.Println(a, b) //2 1
    fmt.Println(swap2(c, d)) //2 1
}

`

数组

  1. [10]int和[20]int是不同类型

  2. 调用func f(arr [10]int)会拷贝数组

  3. 在go语言中一般不直接使用数组,使用切片

`

package main

import "fmt"

func printArray(arr [5]int) {
    for i := 0; i < len(arr); i++ {
        fmt.Println(arr[i])
    }

    for i := range arr {
        fmt.Println(arr[i])
    }

    for i, v := range arr { //索引和值
        fmt.Println(i, v)
    }
}

func main() {
    var arr1 [5]int
    arr2 := [3]int{1,2,3}
    arr3 := [...]int{1,2,3,4,5}
    var grid [2][3]int

    fmt.Println(arr1,arr2,arr3,grid)
    //[0 0 0 0 0] [1 2 3] [1 2 3 4 5] [[0 0 0] [0 0 0]]

    printArray(arr1)
    printArray(arr3)
}

`

Map

  1. 使用range遍历key ,或者遍历key, value对
  2. 不保证遍历顺序,如需顺序,需手动对key排序
  3. 使用len获得元素个数
  4. map使用哈希表,必须可以比较相等
  5. 除了slice, map, function的内建类型都可以作为key
  6. Struct类型不包含上述字段,也可作为key

`

package main

import "fmt"

func main() {
    m1 := map[string]string {
        "qwe":"qe",
        "qq": "ww",
    }

    m2 := make(map[string]int) //m2 == empty map

    var m3 map[string]int //m3 == nill

    fmt.Println(m1,m2,m3) //map[qwe:qe qq:ww] map[] map[]

    for k, v := range m1 {
        fmt.Println(k,v)
    }
    //qwe qe
    //qq ww

    qwName, ok := m1["qwe"] //有值ok为true,反之为false
    fmt.Println(qwName, ok) //qe true
    if ok {
        fmt.Println(qwName) //qe
        fmt.Println(m1["asa"]) //空
    }

    delete(m1, "qwe") //删除key为"qwe"的键值对
    fmt.Println(m1) //map[qq:ww]
}

`

切片splice

  1. 添加元素时如果超越cap ,系统会重新分配更大的底层数组

  2. 由于值传递的关系,必须接收append的返回值

  3. S = append(S, val)

image

`

package main

import "fmt"

func printSlice(s []int) {
    fmt.Printf("slice=%v len=%d cap=%d\n", s, len(s), cap(s))
}

func main() {
    arr := [...]int{0,1,2,3,4,5,6,7}
    s1 := arr[2:6]
    fmt.Println(s1) //[2 3 4 5]  左闭右开

    s2 := s1[3:5]
    fmt.Println(s2) //[5 6] 出现了s1中没有出现的值,因为有cap的缘故

    s3 := s1[:3]
    s1[0] = 100
    fmt.Println(arr) //[0 1 100 3 4 5 6 7]     slice中改变会导致原数组中的值改变
    fmt.Println(s1) //[100 3 4 5]
    fmt.Println(s3) //[100 3 4]

    s4 := append(s1,10)
    fmt.Println(s4) //[100 3 4 5 10]
    s5 := append(s4, 11)
    fmt.Println(s5) //[100 3 4 5 10 11]
    s6 := append(s5, 12)
    fmt.Println(s6) //[100 3 4 5 10 11 12]
    s7 := append(s6,20)
    fmt.Println(s7) //[100 3 4 5 10 11 12 20]
    fmt.Println(s1) //[100 3 4 5]


    //slice的创建方式
    var s []int
    for i := 0; i < 100 ; i++ {
        s = append(s, i)
    }
    fmt.Println(s) //[1,2,3,4.......99]

    a1 := []int{2,3,4,5}
    printSlice(a1) //slice=[2 3 4 5] len=4 cap=4

    a2 := make([]int, 16)
    printSlice(a2) //slice=[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] len=16 cap=16
    a3 := make([]int, 16, 31)
    printSlice(a3) //slice=[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] len=16 cap=31

    copy(a2, a1)//拷贝
    printSlice(a2) //slice=[2 3 4 5 0 0 0 0 0 0 0 0 0 0 0 0] len=16 cap=16

    a2 = append(a2[:2],a2[3:]...) //删除其中某一个元素
    printSlice(a2) //slice=[2 3 5 0 0 0 0 0 0 0 0 0 0 0 0] len=15 cap=16

    //删去头部元素 ,因为是头部元素,pre指针只会往后移动,不会往前,所以,cap会少1,如果是尾部元素,cap不变
    a2 = a2[1:]
    printSlice(a2) //slice=[3 5 0 0 0 0 0 0 0 0 0 0 0 0] len=14 cap=15

    a2 = a2[:len(a2)-1] //删去尾部元素
    printSlice(a2) //slice=[3 5 0 0 0 0 0 0 0 0 0 0 0] len=13 cap=15
}

`

结构体

  1. 首字母大写是public,能被外部访问
  2. 首字母小写是private,不能被外部访问
  3. 命名一般都是CamelCase

`

package main

import (
    "fmt"
)

type treeNode struct {
    value int
    left, right *treeNode
}

//---------
//这两个写法是一样的,(node treeNode)放在前面是将root作为接收者
func (node treeNode) print() { //为结构体定义的方法
    fmt.Println(node.value)
}
func print1(node treeNode) {
    fmt.Println(node.value)
}
//-----------

//因为go语言都是值传递,所以要改变值,得用引用传递
//只有指针才可以改变结构内容
func (node *treeNode) setValue(value int) {//为结构体定义的方法
    node.value = value
}


//使用了自定义工厂函数,注意返回了局部变量的地址,go中不算错
func createTreeNode(value int) *treeNode  {
    return &treeNode{value: value}
}

func main() {
    var root treeNode

    root = treeNode{value: 3}
    root.left = &treeNode{} //因为left是个指针,所以得取地址
    root.right = &treeNode{4, nil, nil}
    root.left.right = new(treeNode)
    root.right.left = createTreeNode(5)

    root.right.left.print() //5
    root.right.left.setValue(9)
    root.right.left.print() //9

    nodes := []treeNode {
        {value:3},
        {},
        {6,nil,&root},
    }
    fmt.Println(nodes) //[{3 <nil> <nil>} {0 <nil> <nil>} {6 <nil> 0xc00000a080}]

}

`