美文网首页js css html
二. Go(Go基础知识)

二. Go(Go基础知识)

作者: 冰菓_ | 来源:发表于2022-07-11 00:24 被阅读0次

    一. Go基础语法

    变量的定义

    使用var关键字,可以放在函数内,或者直接放在包内,使用var()集中定义变量,使用:=可以简化定义,但是只能在函数内使用

    package main
    
    import "fmt"
    
    //没有全局变量的概念,使用var()这种写法/
    
    var (
        a = 1
        b = 2
    )
    
    func variableZeroValue() {
        var a int
        var b int
        fmt.Printf("%d %q\n", a, b)
    }
    
    func variableInitialValue() {
        var a, b int = 1, 2
        fmt.Println(a + b)
    }
    
    func variableTypeDeduction() {
    
        var a, b, c, d = 1, false, "2", true
        //可以简写 := /
        e := "111"
        b = true
        fmt.Println(a, b, c, d, e)
    }
    func main() {
        fmt.Println("hello world")
        variableZeroValue()
        variableInitialValue()
        variableTypeDeduction()
        fmt.Println(a + b)
    }
    
    内建变量类型
    1. bool,string(布尔,字符串)
    2. (u)int,(u)int8,(u)int16,(u)int32,(u)int64,uintptr 加u有符号整数 不加u无符号整数 如果不规定长度 int 还是 int8 还是int32 默认按照操作系统位数 来决定
    3. byte,rune byte字节 8位,rune是4个字节相当于java里的char 但是char是一个字节考虑到国际化原因rune竟可能能放下各种编码 32位 Unicode是2个字节 16位 utf-8是三个字节 24位。
    4. float32,float64,complex64,complex128 浮点数32位和64位,复数 complex64 实部和虚部 分别是 32位 (var a complex64=3 +4i) 128位以此类推。

    强制类型转换
    类型转换必须是强制的

    func trigonometric() {
        a, b := 3, 4
        var c int
        c = int(math.Sqrt(float64(a*a + b*b)))
        fmt.Println(c)
    }
    
    常量与枚举

    const数值可作为各种类型使用(var c int = int(math.Sqrt(aa + bb)))由于常亮没指定类型 它可以自己转换类型。

    枚举类型
    注意自增公式的使用

    func enums() {
        const (
            cpp   = 1
            java  = 2
            scala = 3
            lua   = 4
        )
        fmt.Println(cpp, java, scala, lua)
    
        const (
            b = 1 << (10 * iota)
            kb
            mb
            gb
            tb
        )
        fmt.Println(b, kb, mb)
    }
    
    条件语句

    if
    注意:if 可以像其他语言中的for循环一样的表达式进行判断

    package main
    
    import (
        "fmt"
        "io/ioutil"
    )
    
    func main() {
        const filename = "D:\\玩游戏和安装软件的.txt"
        //从源码中能够看到 ,"io/ioutil 下面的 ioutil.ReadFile方法会返回两个值,[]byte 文件内容,error错误信息/
        contents, err := ioutil.ReadFile(filename)
        if err != nil {
            fmt.Println(err)
        } else {
            fmt.Printf("%s\n", contents)
        }
        //if 可以像其他语言中的for循环一样的表达式进行判断/
        if contents, err := ioutil.ReadFile(filename); err != nil {
            fmt.Println(err)
        } else {
            fmt.Printf("%s\n", contents)
        }
    }
    

    switch
    GO语言和其他语言对比switch会自动有break,如果我们需要执行后面的 case,可以使用 fallthrough

    Go语言追求简洁优雅,所以,Go语言不支持传统的 try…catch…finally 这种异常,因为Go语言的设计者们认为,将异常与控制结构混在一起会很容易使得代码变得混乱。因为开发者很容易滥用异常,甚至一个小小的错误都抛出一个异常。在Go语言中,使用多值返回来返回错误。不要用异常代替错误,更不要用来控制流程。在极个别的情况下,才使用Go中引入的Exception处理:defer, panic, recover。

    func eavl(a, b int, op string) int {
        var re int
        switch op {
        case "+":
            return a + b
        case "*":
            return a * b
        default:
            panic("不支持的运算符" + op)
        }
        return re
    }
    

    注意可以没有expression

    func grade(a int) int {
        var re = 0
        switch {
        case a <= 0:
            return 1
        case a > 0 && a <= 100:
            return a
        case a > 100:
            return 100
        default:
            fmt.Println("其他的....")
        }
        return re
    }
    

    for
    注意for实现while的用法

    package main
    
    import (
        "bufio"
        "fmt"
        "os"
        "strconv"
        "time"
    )
    
    func sumTen() int {
        sum := 0
        for a := 10; a > 0; a-- {
            sum += a
        }
        return sum
    }
    
    func converToBin(n int) string {
        var re string = ""
        for ; n > 0; n /= 2 {
            lsb := n % 2
            re = strconv.Itoa(lsb) + re
        }
        return re
    }
    
    func readFile(filename string) {
        file, err := os.Open(filename)
        if err != nil {
            panic(err)
        } else {
            scanner := bufio.NewScanner(file)
            for scanner.Scan() {
                fmt.Println(scanner.Text())
            }
        }
    
    }
    
    func whileLike() {
        for {
            fmt.Println("死循环")
            time.Sleep(100)
        }
    }
    
    func main() {
        fmt.Println(sumTen())
        fmt.Println(converToBin(13))
        readFile("D:\\玩游戏和安装软件的.txt")
        whileLike()
    }
    
    函数
    package main
    
    import (
        "fmt"
        "math"
        "reflect"
        "runtime"
    )
    
    func div(a, b int) (int, int) {
        return a / b, a % b
    }
    
    func div1(a, b int) (q, r int) {
        return a / b, a % b
    }
    
    //函数式编程/
    func apply(op func(int, int) int, a, b int) int {
        pointer := reflect.ValueOf(op).Pointer()
        name := runtime.FuncForPC(pointer).Name()
        fmt.Println(name, a, b)
        return op(a, b)
    }
    
    //返回多个参数/
    func main() {
        fmt.Println(div(13, 3))
        q, r := div1(13, 3)
        fmt.Println(q, r)
    
        fmt.Println(apply(func(i int, i2 int) int {
            return int(math.Pow(float64(i), float64(i2)))
        }, 3, 4))
    }
    

    可变参数

    func sunArgs(values ...int) int {
        sum := 0
        for i := range values {
            sum += values[i]
        }
        return sum
    }
    
    指针

    值传递和引用传递?
    Go语言只有值传递一种....这一块我在Java那里就没怎么搞清楚,后来学了点scala似乎搞清楚了,主要在于情景的应用

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

    二. 内建容器

    数组(golang 一般不用的,数组是值传递)
    package main
    
    import (
        "fmt"
    )
    
    //数组是值类型
    func changeArray(arr [3]int) {
        arr[0] = 100
        for k, v := range arr {
            fmt.Println(k, v)
        }
    }
    
    func changeArray1(arr *[3]int) {
        arr[0] = 100
        for k, v := range arr {
            fmt.Println(k, v)
        }
    }
    func main() {
        var arr1 [5]int
        fmt.Println(arr1)
        arr2 := [3]int{1, 2, 3}
        fmt.Println(arr2)
        var arr3 = [...]int{2, 4, 6}
        fmt.Println(arr3)
        var grid [5][5]int
        fmt.Println(grid)
        //遍历
        for k, v := range arr2 {
            fmt.Println(k, v)
        }
        changeArray(arr2)
        for k, v := range arr2 {
            fmt.Println(k, v)
        }
        changeArray1(&arr2)
        for k, v := range arr2 {
            fmt.Println(k, v)
        }
    }
    
    切片👍

    ???下面两个又什么区别,为什么这种写法[]是值传递的....(教材有误)
    arr := []int{0, 1, 2, 3, 4, 5, 6, 7}
    arr := [...]int{0, 1, 2, 3, 4, 5, 6, 7}

    解决:区分数组的声明和切片的声明方式 当使用字面量来声明切片时,其语法与使用字面量声明数组非常相似。二者的区别是:如果在 [] 运算符里指定了一个值,那么创建的就是数组而不是切片。只有在 [] 中不指定值的时候,创建的才是切片。看下面的例子:

    // 创建有 3 个元素的整型数组
    myArray := [3]int{10, 20, 30}
    // 创建长度和容量都是 3 的整型切片
    mySlice := []int{10, 20, 30}
    
    package main
    
    import "fmt"
    
    func take(arr []int) {
        arr[3] = 100
    }
    func main() {
        arr := []int{0, 1, 2, 3, 4, 5, 6, 7}
        s := arr[2:6]
        fmt.Println(s)
        s1 := arr[2:]
        fmt.Println(s1)
        s2 := s[1:3]
        fmt.Println(s2)
        take(arr)
        fmt.Println(arr)
        fmt.Println(s1)
        fmt.Println(s2)
    
        arr1 := []int{0, 1, 2, 3, 4, 5, 6, 7}
        s11 := arr1[2:6]
        s22 := s11[3:5]
        fmt.Printf("len=%d cap=%d slice=%v\n", len(s11), cap(s11), s11)
        fmt.Printf("len=%d cap=%d slice=%v\n", len(s22), cap(s22), s22)
    
    }
    
    slice的扩展

    slice可以向后扩展,但是不可以向前扩展

    向slice添加元素
    添加元素时如果超越cap,系统就会重新分配更大的底层数组
    由于值传递的关系,必须接收append的返回值

    package main
    
    import "fmt"
    
    func show(arr []int) {
        fmt.Println(len(arr), cap(arr))
    
    }
    func main() {
        var arr []int
        for i := 0; i < 100; i++ {
            show(arr)
            arr = append(arr, 2*i)
        }
        fmt.Println(arr)
    
    }
    

    Go 语言切片(Slice) | 菜鸟教程 (runoob.com)
    Golang 入门 : 切片(slice) 👍

    切片之所以被称为切片,是因为创建一个新的切片,也就是把底层数组切出一部分。通过切片创建新切片的语法如下:

    slice[i:j]
    slice[i:j:k]
    

    其中 i 表示从 slice 的第几个元素开始切,j 控制切片的长度(j-i),k 控制切片的容量(k-i),如果没有给定 k,则表示切到底层数组的最尾部。(看一下切片的扩展)

    map

    在向map中存储元素的时候,会将每个key经过hash运算,根据运算得到的hash值选择合适的hash bucket(hash桶),让后将各个key/value存放到选定的hash bucket中。如果一来,整个map将根据bucket被细分成很多类别,每个key可能会交叉地存放到不同的bucket中。所以,map中的元素是无序的,遍历时的顺序是随机的,即使两次以完全相同的顺序存放完全相同的元素,也无法保证遍历时的顺序。由于要对key进行hash计算选择hash bucket,所以map的key必须具有唯一性,否则计算出的hash值相同,将人为出现hash冲撞。在访问、删除元素时,也类似,都要计算key的hash值,然后找到对应的hash bucket,进而找到hash bucket中的key和value。Go中的map是一个指针,它的底层是数组,而且用到了两个数组,其中一个更底层的数组用于打包保存key和value。

    package main
    
    import "fmt"
    
    func main() {
        m1 := map[int]string{
            1: "上海",
            2: "北京",
        }
        m2 := make(map[string]string)
        var m3 = map[int]string{}
        fmt.Println(m1, m2, m3)
        for _, v := range m1 {
            fmt.Println(v)
        }
    
        if v, Exist := m1[10]; Exist {
            fmt.Println(v)
        } else {
            fmt.Println("not exist")
        }
    
        delete(m1, 1)
        var _, OK = m1[1]
        fmt.Println(OK)
    
    }
    
    func main() {
        mf := map[int]func() int{
            1: func() int { return 10 },
            2: func() int { return 20 },
            5: func() int { return 50 },
        }
        fmt.Println(mf)  // 输出函数的指针
        a := mf[1]()     // 调用某个分支的函数
        println(a)
    }
    

    寻找最长不含有重复字符的子串

    package main
    
    import "fmt"
    
    func take_num(st string) int {
        //字符对应的最新索引
        m := map[byte]int{}
        start := 0
        st_lenth := 0
        bytes := []byte(st)
        for i, ch := range bytes {
            // ok && lastI >= start ,ok是因为在map中存在的话就更新,与此同时lastI >= start保证了小的ch值不能小于start
            if lastI, ok := m[ch]; ok && lastI >= start {
                start = lastI + 1
            }
            if i-start+1 > st_lenth {
                st_lenth = i - start + 1
            }
            m[ch] = i
        }
    
        return st_lenth
    
    }
    
    func main() {
        fmt.Println(take_num("abcbod"))
    }
    

    三. 面对对象

    结构体和方法
    package main
    
    import "fmt"
    
    type treeNode struct {
        value       int
        left, right *treeNode
    }
    
    // 自定义工厂方法
    func createTreeNode(value int) *treeNode {
        return &treeNode{value: value}
    }
    
    //为结构定义方法,显示定义和命名方法的接收者
    func (node treeNode) print() {
        fmt.Println(node.value)
    }
    
    //值传递
    func (node treeNode) setValue(value int) {
        node.value = value
    }
    func (node *treeNode) setValue1(value int) {
        node.value = value
    }
    
    // 树的遍历
    func (node *treeNode) traverse() {
        if node == nil {
            return
        }
        node.left.traverse()
        node.print()
        node.right.traverse()
    }
    
    func main() {
        var r treeNode
        r = treeNode{value: 1}
        var _ = treeNode{left: new(treeNode)}
        r.left = &treeNode{}
        r.right = &treeNode{2, nil, nil}
        r.right.left = new(treeNode)
        fmt.Println(r)
    
        r.left.left = createTreeNode(3)
        r.print()
    
        fmt.Println(r.left.left)
        r.setValue(100)
        fmt.Println(r.value) //还是3
        r.setValue1(100)
        fmt.Println(r.value) //100
    
        r.traverse()
    }
    
    值接收者和指针接收者
    1. 要改变内容必须要使用指针接收者
    2. 结构过大也考虑指针接收者
    3. 一致性问题:如果有指针接收者,最好都是指针接收者
    4. 值/指针接收者均可接收值/指针
    封装
    扩展已有类型
    • 定义别名
    • 使用组合
      在 Go 中,通过在结构体内嵌套结构体,可以实现组合,组合的典型例子就是博客帖子。每一个博客的帖子都有标题、内容和作者信息。使用组合可以很好地表示它们
      golang的组合
    使用内嵌来扩展已有结构

    这块有点混乱....改天再看看吧...
    Go学习:使用内嵌来扩展已有类型

    四. 依赖管理

    GOPATH和GOVENDOR
    GO Mod

    golang中使用GOPATH模式和GoModule

    相关文章

      网友评论

        本文标题:二. Go(Go基础知识)

        本文链接:https://www.haomeiwen.com/subject/tifibrtx.html