2、Golang基础--包的使用、if-else语句、循环、switch语句、数组、切片、可变函数参数、map类型
阅读原文时间:2023年07月10日阅读:3
// 为了便于组织代码,同一种类型的代码,写在同一个包下,便于管理
// 定义包
    -新建一个文件夹
    -内部有很多go文件
    -在每个go文件的第一行,都要声明包名,并且包名必须一致
    -在一个文件夹(同级)下只能有一个包
    -在同一个包下,变量和函数都是共享的(一个包相当于一个go文件)
    -在一个包下,不能重复定义变量和函数
    -除了main包,其他都是用来被导入使用的
    -无论是函数,还是变量,大写字母开头表示导出,可以在其他包使用
    -尽量包名就是文件夹名

// 老版本的gopath和现在的go moduls的区别
        -1.11后才支持go moduls模式,现在都用
        -如果使用go path开发,所有项目的代码,必须放在 go path路径下的 src文件夹下,否则找不到,包括包,包括下载的第三方包---》弃用了
        -使用go mod以后,现在推荐的
            -代码不需要放在go path路径下的 src文件夹下了,放在任何路径下都可以
            -项目路径下必须要有个go.mod
            -go mod init

// 把go path 修改成go mod项目
        //go env -w 修改go的环境变量
        -可以移动目录
        -go mod init 项目名
            -在项目路径下生成 go.mod,自动生成,不要去动
            -内容:
                module 项目名
                go 1.17
        -如果有第三方包,需要再执行一下
            -go mod tidy  // 会去下载第三方包


// 逻辑判断

package main

import "fmt"

// if-else的使用
// if 后跟条件,符合条件会执行,代码用{}包裹
// else if 后跟条件,符合条件会执行,代码用{}包裹
// else 不需要加条件,用{}包裹
// {}的{需要和关键字在一行

func main() {  // 大括号的 { 不能换到下一行
    number := 55
    if number >= 90 && number <= 100 {
        fmt.Println("优秀")
    } else if number >= 60 && number <= 90 {
        fmt.Println("良好")
    } else {
        fmt.Println("不及格")
    }
}



// go 语言中循环使用for关键字
    -基于迭代的循环
    -基于索引的循环



package main

import "fmt"

// for 循环的使用
// for 后面三段,分号隔开
// 第一段:索引的开始
// 第二段:条件
// 第三段:自增或自减

func main() {
    // 1 基于索引的循环
    // 1.1 完整版
    // 循环打印出0-9
    for i := 0; i < 10; i++ {
        fmt.Println(i)
    }

    // 1.2 省略第一段: i的作用域变大
    var i2 = 0
    for ; i2 < 10; i2++ {
        fmt.Println(i2)
    }

    // 1.3 省略第三段,自增写在循环体内部
    for i3 := 0; i3 < 10; {
        fmt.Println(i3)
        i3++
    }

    // 1.4 省略第一段和第三段,
    i4 := 0
    for ; i4 < 10; {
        fmt.Println(i4)
        i4++
    }

    // 简写为:
    for i4 < 10 {
        fmt.Println(i4)
    }
    /*
    // 死循环
    for true {
        fmt.Println("死循环1")
    }
    */

    /*
    // 1.5 三部分全省略
    for ;;{
        fmt.Println("死循环2")
    }

    // 简写成:
    for {
        fmt.Println("死循环3")
    }
    */

    // 2 基于迭代的循环
    var a = [3]int{1, 25, 34}
    for i5, value := range a {
        fmt.Println(i5)
        fmt.Println(value)
    }

    // 用基于索引实现
    var a6 = [3]int{1, 25, 34}
    for i6 := 0; i6 < len(a6); i6++ {
        fmt.Println(i6)
        fmt.Println(a6[i6])
    }
}



// 可以优雅的替换掉if-else
package main

import "fmt"

// switch使用
func main() {

    // 1 使用方式一
    name := "cx"
    switch name {
    case "cx":
        fmt.Println("11")
    case "lqz":
        fmt.Println("12")
    }

    // 2 使用方式二:switch后不跟值、case后跟
    name2 := "cx"
    switch {
    case name2 == "cx":
        fmt.Println("21")
    case name2 == "aa":
        fmt.Println("22")
    }

    // 3 使用方式三:多条件()
    // 3.1:,表示或者
    name3 := "cx"
    switch name3 {
    case "cx", "cx2":
        fmt.Println("311")
    case "lqz", "lqz2":
        fmt.Println("312")
    }

    // 3.2:||表示或者  &&表示并且
    name32 := 10
    switch {
    case name32 == 11 || name32 == 1:
        fmt.Println("321")
    case name32 > 1 && name32 < 3:
        fmt.Println("322")
    case name32 == 1, name32 == 2, name32 == 10:
        fmt.Println("333")
    }

    // 4 使用方式4:default的使用,当所有case都不满足后执行
    name4 := "cx"
    switch name4 {
    case "cx2":
        fmt.Println("cx")
    case "lqz2":
        fmt.Println("lqz")
    default:
        fmt.Println("没匹配上")
    }

    // 5 fallthrough的使用:无条件执行下一个case  只能放在语句结尾
    name5 := "cx"
    switch name5 {
    case "cx":
        fmt.Println("cx")
        fallthrough
    case "lqz":
        fmt.Println("lqz")
    default:
        fmt.Println("没匹配上")
        // 输出:cx lqz
    }
}



// 类型,变量,连续存储数据,数据类型是一致的,类似于python中的列表(但列表可放不同类型数据)
package main

import "fmt"

// 数组(array)
func main() {
    // 1 数组的定义,定义时,大小就固定了,后期不能修改大小
    var a [3]int = [3]int{4, 5, 6}
    fmt.Println(a)

    // 2 使用:修改 取值
    // 根据下标使用
    a[0] = 99
    fmt.Println(a)
    fmt.Println(a[0])
    //fmt.Println(a[3])  // 报错

    // 3 其他定义方式
    var a3 = [3]int{4} // 可以少(如果不足的元素值为默认值0),但不能多,否则报错
    fmt.Println(a3)

    a4 := [3]int{1, 2, 3}
    fmt.Println(a4)

    a5 := [30]int{28: 1} // 指定位置赋初值 指定索引28对应值为1、其他为默认值0
    fmt.Println(a5)

    var a6 [3]int
    fmt.Println(a6) // [0, 0, 0]

    var a7 = [...]int{3, 4, 5} // 虽然使用...初始化,但是长度也是固定的,根据值多少个确定长度
    fmt.Println(a7)            // [3 4 5]

    var a8 = [...]int{50: 99}
    fmt.Println(a8)
    fmt.Printf("%T", a8) // 长度为51

    // 4 数组长度
    b := [3]int{1, 2, 3}
    fmt.Println(len(b)) // len()不需要导入包、直接使用即可

    for i := len(b) - 1; i >= 0; i-- {
        fmt.Println(a[i])
    }

    // 5 数组的循环
    // 2.1 基于索引的循环
    for i := 0; i < len(b); i++ {
        fmt.Println(b[i])
    }

    // 2.2 反向取
    for i := len(b) - 1; i>=0; i-- {
        fmt.Println(b[i])
    }

    // 5.2 基于迭代的循环
    // range是一个关键字
    // 返回一个值是索引、返回两个值是索引和值
    for i := range b {
        fmt.Println(b[i])
    }

    for _, value := range b {  // 变量定义后必须使用,但是用_接收可以不使用
        fmt.Println(value)
    }

    // 6 多维数组
    var b2 [3][4]int = [3][4]int{{3, 3, 3, 3}, {4, 4, 4}, {5}}
    fmt.Println(b2)

    // 6.1 循环(两层循环)
    for _, value := range b2 {
        for _, value2 := range value {
            fmt.Println(value2)
        }
    }
}



// 切片是由数组建立的一种方便、灵活且功能强大的包装,切片本身不拥有任何数据。它们只是对现有数组的引用(指针指向数组)


package main

import "fmt"

// 切片(slice)
func main() {
    // 1 基于数组,定义切片
    a1 := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    var s []int // 中括号不带任何东西,就是切片类型
    s = a1[:]   // 把数组从头到尾的引用给s
    fmt.Println(a1, s)
    fmt.Printf("a的类型是%T,值是%v", a1, a1)
    fmt.Println() // 输入空行(换行)
    fmt.Printf("s的类型是%T,值是%v", s, s)
    fmt.Println()

    // 2 使用,取值,改值
    fmt.Println(s[0]) // 1
    //fmt.Println(s[100])  // 编译不报错,执行会报错

    s[1] = 999
    fmt.Println(s) // [1 999 3 4 5 6 7 8 9 10]

    // 3 切片的变化,会影响底层数组
    a3 := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    var s3 []int = a3[:]
    s3[0] = 999
    fmt.Println("s3:", s3) // s3: [999 2 3 4 5 6 7 8 9 10]
    fmt.Println("a3:", a3) // a3: [999 2 3 4 5 6 7 8 9 10]

    // 4 底层数组的变化也会影响数组
    a4 := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    // 简写:var s4 []int = a3[:]
    var s4 = a4[:]
    s4[0] = 9999
    fmt.Println("s4:", s4) // s3: [9999 2 3 4 5 6 7 8 9 10]
    fmt.Println("a4:", a4) // a3: [9999 2 3 4 5 6 7 8 9 10]

    // 5 切片的长度(len)和容量(cap)
    // 数组有长度、并且长度不能变
    // 切片也有长度、但是长度可以变,切片有容量
    a5 := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    var s5 = a5[0:3] // 顾头不顾尾:索引0、1、2被引用,没有3
    fmt.Println(s5)
    fmt.Println(len(s5)) // 长度为3
    fmt.Println(cap(s5)) // 容量为10(这个切片最多能存多少值,基于底层数组来的)

    // 6 长度和容量再次研究
    a6 := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    s6 := a6[2:9]
    fmt.Println(s6)
    fmt.Println(len(s6)) // 7
    fmt.Println(cap(s6)) // 8  容量是底层数组决定的,但是不一定是底层数组的大小,得看切片从哪个位置开始引用数组,从切片引用数组的开始位置到结束,是容量的大小

    // 7 通过内置函数make创建切片
    // make(类型,长度,容量)
    var s7 []int = make([]int, 3, 4) // 简写:var s7 = make([]int,3,4)
    fmt.Println(len(s7))             // 3
    fmt.Println(cap(s7))             // 4

    // 定义切片并初始化
    var ss = []int{2, 3, 4}
    fmt.Println(len(ss)) // 3
    fmt.Println(cap(ss)) // 3

    // 8 追加切片
    var s8 = make([]int, 3, 4)
    fmt.Println(s8)
    fmt.Println(len(s8)) // 3
    fmt.Println(cap(s8)) // 4

    s8 = append(s8, 99)
    fmt.Println(s8)
    fmt.Println(len(s8)) // 4
    fmt.Println(cap(s8)) // 4

    // 注意:此时长度已经达到容量值,如继续追加,切片容量会翻倍、自动扩容

    s8 = append(s8, 88)
    fmt.Println(s8)
    fmt.Println(len(s8)) // 5
    fmt.Println(cap(s8)) // 8

    // 9 追加切片后,底层数组如何变化?
    a9 := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    s9 := a9[2:9]
    fmt.Println(s9)

    // 9.1 切片变,底层数组变
    s9[0] = 666
    fmt.Println(s9) // [666 4 5 6 7 8 9]
    fmt.Println(a9) // [1 2 666 4 5 6 7 8 9 10]

    // 9.2 数组变,切片也会变
    a9[8] = 999
    fmt.Println(a9) // [1 2 666 4 5 6 7 8 999 10]
    fmt.Println(s9) // [666 4 5 6 7 8 999]

    // 9.3 追加元素(追加到临界状态,再追加,数组就不够了),底层数组会跟着变
    //      底层数组不够了,go语言会自动申请一个新的数组,大小为原来切片容量的2倍,把原来切片的值,复制到新数组上。
    //      此时,切片与原来数组脱离,原来数组的变化不再会影响切片了
    s9 = append(s9, 93)
    fmt.Println(s9) // [666 4 5 6 7 8 999 93]
    fmt.Println(a9) // [1 2 666 4 5 6 7 8 999 93]

    fmt.Println(len(s9)) // 8
    fmt.Println(cap(s9)) // 8
    s9 = append(s9, 94)  // 此时,切片容量不够,切片与原来数组脱离
    fmt.Println(s9)      // [666 4 5 6 7 8 999 93 94]
    fmt.Println(len(s9)) // 9
    fmt.Println(cap(s9)) // 16

    s9[0] = 10000
    fmt.Println(s9) // [10000 4 5 6 7 8 999 93 94]
    fmt.Println(a9) // [1 2 666 4 5 6 7 8 999 93] 不会再跟着变化了

    // 切片就是个寄生虫,有了新宿主,就和原来宿主没关系了

    // 10 切片的函数传递
    /*
        数组是值类型,切片是引用类型(指针)
        go语言中的参数传递是:copy传递,也就是把变量复制一份到函数中
            如果是值类型,复制一个新的值传入,修改新值不会影响原来的值
            如果是引用类型,复制这个引用传入(指向没变),修改新值会影响原来的值
    */
    var a10 = [3]int{4, 5, 6}
    test1(a10)       // test函数定义在最下方
    fmt.Println(a10) // a10不会变 [4 5 6]

    var s10 = a10[:]
    test2(s10)
    fmt.Println(s10) // s值会变,因为s是个引用 [999 5 6]

    /* 补充:
    Python与其他语言区别:
        其他语言(如Go):分为值类型、引用类型
        Python:一切皆对象、一切皆引用
            解决方式:分为 可变类型、不可变类型
            可变类型:列表、字典
                函数中修改会影响到原来的值
            不可变类型:字符串、元组、数字
                函数中修改不会影响到原来的、如要改加global声明
    */

    // 11 多维切片
    var s11 = [][]int{{1}, {2, 2}, {3, 3, 3}}
    fmt.Println(s11)
    fmt.Println(len(s11)) // 3
    fmt.Println(cap(s11)) // 3

    fmt.Println(len(s11[0])) // 1
    fmt.Println(cap(s11[0])) // 1

    fmt.Println(len(s11[1])) // 2
    fmt.Println(cap(s11[1])) // 2

    // 11.2 通过make初始化多维切片
    var s112 = make([][]int, 3, 4)   // 内层的切片没有初始化,只要使用内层切片,内层所有的切片都要初始化才行
    fmt.Println("s112[0]:", s112[0]) // 是个没初始化的切片 s112[0]: []
    s112[0] = make([]int, 5, 6)      // 初始化内层切片
    s112[0][0] = 99
    fmt.Println(s112[0]) // [99 0 0 0 0]
    // fmt.Println(s112[0][5])  // 报错:切片越界,虽然容量为6,但还没使用到(长度为5),就不能取
    // 解决:用append在切片末尾再加一个元素,让长度变为6即可

    // 11.3 循环多维切片
    // 基于索引
    var s113 = [][]int{{1}, {2, 2}, {3, 3, 3}}
    for i := 0; i < len(s113); i++ {
        for i2 := 0; i2 < len(s113[i]); i2++ {
            fmt.Println(s113[i][i2])
        }
    }

    // 基于迭代
    for _, v := range s113 {
        for _, v2 := range v {
            fmt.Println(v2)
        }
    }

    // 12 copy:把一个切片,复制到另一个切片上
    var a12 = [10000]int{3, 4, 5}
    fmt.Println(a12) //[3 4 5 0 0 0 0 ... ]
    s12 := a12[:3]
    fmt.Println(s12) // [3 4 5]

    // 使用s12会基于一个很大的数组,内存占用高
    // 解决:将s12 copy到另一个基于小数组的切片上
    s121 := make([]int, 3, 3)
    fmt.Println(s121) // [0 0 0]
    copy(s121, s12)
    fmt.Println(s121) // [3 4 5] ,以后都用s121操作,节约了内存

    // 12.2 俩切片一样长,可以copy,如果不一样长呢?
    s1221 := make([]int, 5, 5)
    copy(s1221, s12)
    fmt.Println(s1221) // [3 4 5 0 0]  多了的用默认值0填充

    s1222 := make([]int, 2, 2)
    copy(s1222, s12)
    fmt.Println(s1222) // [3 4]  少了的则截断
}

func test1(a [3]int) {
    a[0] = 999
    fmt.Println("test1:", a)
}

func test2(a []int) {
    a[0] = 999
    fmt.Println("test2:", a)
}



package main

import "fmt"

// 可变长函数参数:...类型

func main() {

    test3(2, 3, 4, 5)       // [2 3 4 5]  类型:[]int
    test4("aa", "bb", "cc") // [aa bb cc] 类型:[]string

    a := [3]int{3, 4, 5}
    fmt.Println(1, 2, "aa", a) // Println函数中形参类型为:...interface{}  空接口类型,可以接收任意类型

    s := []int{1, 2, 3}
    // test3(s)  // 报错
    test3(s...) // 相当于将s打散后传入函数 结果:[1 2 3] 类型:[]int

    // 如果既想无限接收number类型、又想无限接收string类型。可以自定义类型。

}

func test3(a ...int) { // 可以接收任意长度参数、但类型必须一致
    fmt.Println(a)
    fmt.Println()
    fmt.Printf("类型:%T", a) // 切片类型 []int
}

func test4(a ...string) {
    fmt.Println(a)
    fmt.Println()
    fmt.Printf("类型:%T", a) // 切片类型 []string
    // 取到第几个参数
    fmt.Println("第一个参数:", a[0])
}



map 是在 Go 中将值(value)与键(key)关联的内置类型。通过相应的键可以获取到值(Python的字典类型)
value值可以任意,但是go中value值必须都一致
key值只能用数字,字符串,布尔,key值也固定


package main

import "fmt"

// map的使用

func main() {
    //1 map的定义
    var m map[string]int // map[key类型]value类型
    fmt.Println(m)       // 只定义,没有初始化,零值也是nil
    if m == nil {
        fmt.Println("map没有初始化")
    }
    // 第一个参数传类型,map有长度没有容量
    var m2 = make(map[string]int)
    fmt.Println(m) //map[]  已经不是nil了,它可以直接用了,因为初始化了
    if m2 == nil {
        fmt.Println("m2没有初始化")
    } else {
        fmt.Println("m2已经初始化了")
    }

    /*
        补充  数字,字符串,布尔,数组,切片,map类型的0值
        值类型:有自己的0值,
            数字:0,
            字符串:""
            布尔:false
            数组:数组里元素类型的零值
        引用类型:零值是 nil  (None:python中所有类型的空值都是None)
            切片
            map
    */
    // 值类型验证
    var a1 float32
    var a2 string
    var a3 bool
    var a4 [4]string
    fmt.Println(a1, a2, a3, a4)

    // 引用类型验证
    var ss []int
    fmt.Println(ss) // []
    if ss == nil {
        fmt.Println("空的") // 执行
    }

    var s = make([][]int, 3, 3)
    if s[0] == nil {
        fmt.Println("没有初始化") // 执行
    }
    // s[0][0] = 99 // 报错  nil[0] 相当于Python的 None[0]

    var s1 []int
    fmt.Println(s1)
    if s1 == nil {
        fmt.Println("没有初始化") // 执行
    }

    s2 := make([]int, 0, 4)
    fmt.Println(s2)
    if s2 == nil {
        fmt.Println("没有初始化") // 执行
    }

    // 2 map的定义并初始化
    var m21 map[string]int = map[string]int{"name": 12, "age": 19}
    var m22 = map[string]int{"name": 12, "age": 19} // 简写
    m23 := map[string]int{"name": 12, "age": 19}
    m24 := map[string]int{"name": 12, "age": 19}
    fmt.Println(m21, m22, m23, m24) // map[age:19 name:12] map[age:19 name:12] map[age:19 name:12] map[age:19 name:12]

    // 3 map的使用,取值,赋值
    m3 := map[string]int{"name": 12, "age": 19}
    fmt.Println(m3["age"]) // 取值
    m3["age"] = 99         // 赋值存在的则修改值
    m3["sex"] = 1          // 赋值不存在的则添加值
    fmt.Println(m3)        // map[age:99 name:12 sex:1]

    // 取不存在的值,不报错,取出value值的0值
    fmt.Println(m3["hobby"]) // 0

    //根据key取value,判断value是否存在
    _, ok := m3["hobby"] // 可以使用两个值来接收,第二个值是个布尔值,如果ok为true,说明存在,否则不存在  range
    fmt.Println(ok)      //false
    m3["hobby"] = 66
    _, ok1 := m3["hobby"]
    fmt.Println(ok1) // true

    // 3.2 删除元素  内置函数  只能根据key值删除
    m4 := map[string]int{"name": 12, "age": 19}
    delete(m4, "name")
    delete(m4, "hobby") // 删除不存在的,不会报错
    fmt.Println(m4)

    // 4 map长度  总共有多少个元素
    m5 := map[string]int{"name": 12, "age": 19}
    m5["xx"] = 12
    m5["xx1"] = 12
    m5["xx2"] = 12
    m5["xx3"] = 12
    fmt.Println(len(m5)) // 6
    // fmt.Println(cap(m5))  // 报错,没有容量这一说,map类型可以无限扩容

    // 5 map是引用类型,零值是 nil,引用类型一定要初始化,值类型不需要初始化就有零值
    // 当参数传递
    m6 := map[string]int{"name": 12, "age": 19}
    fmt.Println(m6)
    test5(m6)  // 函数在最下面定义
    fmt.Println(m6) // 会被改掉,应用类型

    // map 之间不能用 == 判断,== 只能用来检查 map 是否为 nil
    /*
        m7 := map[string]int{"name":12,"age":19}
        m8 := map[string]int{"name":12,"age":19}
        fmt.Println(m7==m8)  报错:map can only be compared to nil

        s7:=[]int{4,5,6}
        s8:=[]int{4,5,6}
        fmt.Println(s7==s8)  报错:map can only be compared to nil
    */

    // 引用类型不能比较 ,值类型能比较
    // 数组的长度也是类型的一部分,长度不一样,不是同一个类型
    a9 := [3]int{4, 5, 6}
    a10:=[3]int{4,6,6}
    //a10 := [4]int{4, 6, 6}  // 加了这句则报错、不同类型不能比较
    fmt.Println(a9 == a10) //  false

    // map 可以无限制的添加值,只要内存够用,自动扩容   因为map是引用类型,扩容直接添加新map引用到上一个map即可
    // 数组长度固定,一旦定义不能改变---》为什么?  因为数组是值类型,要定义好放值的大小,后面添加值才好有位置放
}

func test5(m map[string]int) {
    m["age"] = 99
    fmt.Println(m)
}

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章