Go基础语法(四)

作者: kakarotto | 来源:发表于2018-11-16 13:59 被阅读2次

    可变参数函数

    可变参数函数是一种参数个数可变的函数。

    如果函数最后一个参数被记作 ...T ,这时函数可以接受任意个 T 类型参数作为最后一个参数。

    只有函数的最后一个参数才允许是可变的。

    package main
    
    import (
        "fmt"
    )
    
    func find(num int, nums ...int) {
        fmt.Printf("type of nums is %T\n", nums)
        found := false
        for i, v := range nums {
            if v == num {
                fmt.Println(num, "found at index", i, "in", nums)
                found = true
            }
        }
        if !found {
            fmt.Println(num, "not found in ", nums)
        }
        fmt.Printf("\n")
    }
    func main() {
        find(89, 89, 90, 95)
        find(87)
    }
    

    可变参数函数的工作原理是把可变参数转换为一个新的切片

    因此以上代码中这三个参数被编译器转换为一个 int 类型切片 int []int{89, 90, 95} 然后被传入 find函数。

    在上面代码中,find 函数仅有一个参数时,我们没有给可变参数 nums ...int 传入任何参数。这也是合法的,在这种情况下 nums 是一个长度和容量为 0 的 nil 切片。

    给可变参数函数传入切片

    直接给可变参数函数传入切片,这种情况下无法通过编译,编译器报出错误 main.go:23: cannot use nums (type []int) as type int in argument to find 。

    在上边的代码中可以看到,这些可变参数会被转换为 int 类型切片然后再传入 find 函数中,而如果传入的已经是一个 int 类型切片,
    编译器试图在 nums 基础上再创建一个切片,像下面这样:

    nums := []int{89, 90, 95}
    find(89, []int{nums})
    

    这显然是错误的。

    如果非要直接传入切片,有一个可以直接将切片传入可变参数函数的语法糖,你可以在在切片后加上 ... 后缀。如果这样做,切片将直接传入函数,不再创建新的切片。

    func main() {
        nums := []int{89, 90, 95}
        find(89, nums...)
    }
    
    易错点1

    看下边代码:

    package main
    
    import (
        "fmt"
    )
    
    func change(s ...string) {  
        s[0] = "Go"
    }
    
    func main() {
        welcome := []string{"hello", "world"}
        change(welcome...)
        fmt.Println(welcome)
    }
    

    如果使用了 ... ,welcome 切片本身会作为参数直接传入,不需要再创建一个新的切片。这样参数 welcome 将作为参数传入 change 函数

    在 change 函数中,切片的第一个元素被替换成 Go,这样程序产生了下面的输出值:

    [Go world]
    
    易错点2

    继续看代码:

    package main
    
    import (
        "fmt"
    )
    
    func change(s ...string) {
        s[0] = "Go"
        s = append(s, "playground")
        fmt.Println(s)
    }
    
    func main() {
        welcome := []string{"hello", "world"}
        change(welcome...)
        fmt.Println(welcome)
    }
    
    运行结果:
    [Go world playground]
    [Go world]
    

    在最初的时候有一句话:GO 语言的参数传递都是 值传递。
    这里就是考究的对这句话的理解。 还有就是对slice的理解。
    slice 包含 3部分: 长度、容量和指向数组第零个元素的指针 所谓的值传递怎么理解呢,就是传递slice 的时候,把这三个值拷一个副本,传递过去。注意:指针作为值拷贝的副本,指向的是同一个地址,所以修改地址的内容时,原slice也就随之改变。 反之,对拷贝slice副本的修改,如:append,改变的是副本的len、cap,原slice的len、cap并不受影响。

    Maps

    • map 是在 Go 中将键(key)与值(value)关联的内置类型。通过相应的键可以获取到值。
    //创建 map 的语法。
    make(map[type of key]type of value) 
    
    //创建了一个名为 personSalary 的 map,键是 string 类型,而值是 int 类型。
    personSalary := make(map[string]int)
    

    map 的零值是 nil。如果你想添加元素到 nil map 中,会触发运行时 panic。因此 map 必须使用 make 函数初始化。

    package main
    
    import (
        "fmt"
    )
    
    func main() {  
        var personSalary map[string]int
        if personSalary == nil {
            fmt.Println("map is nil. Going to make one.")
            personSalary = make(map[string]int)
        }
    }
    
    给map添加元素

    这里map添加元素和python中dict很像,直接赋值便可:

    func main() {
        var personSalary map[string]int
        if personSalary == nil {
            fmt.Println("map is nil. Going to make one.")
            personSalary = make(map[string]int)
        }
    
        personSalary["num"] = 2
        personSalary["age"] = 24
    
        fmt.Println(personSalary)
    }
    

    你也可以在声明的时候初始化 map:

    package main
    
    import (
        "fmt"
    )
    
    func main() {
        personSalary := map[string]int{
            "steve": 12000,
            "jamie": 15000,
        }
        personSalary["mike"] = 9000
        employee := "jamie"
        fmt.Println("Salary of", employee, "is", personSalary[employee])
    }
    

    如果获取一个不存在的元素,map 会返回该元素类型的零值。

    例如:在 personSalary 这个 map 里,如果我们获取一个不存在的元素,会返回 int 类型的零值 0。

    map中获取不包含指定key时,不会产生运行错误。

    判断map中是否存在指定的key

    value, ok := parMap["g"]
    如果存在,ok=ture,value=对应的其值
    如果不存在,ok=false,value=该map值得类型的零值

    func main() {
    
        parMap := map[string]int{
            "name":2,
            "age":22,
        }
        fmt.Println(parMap)
        value, ok := parMap["g"]
        fmt.Println(value, ok)
        
    }
    
    遍历map

    使用for := range遍历

    for value, key := range parMap

    package main
    
    import (
        "fmt"
    )
    
    func main() {
        personSalary := map[string]int{
            "steve": 12000,
            "jamie": 15000,
        }
        personSalary["mike"] = 9000
        fmt.Println("All items of a map")
        for key, value := range personSalary {
            fmt.Printf("personSalary[%s] = %d\n", key, value)
        }
    
    }
    

    有一点很重要,当使用 for range 遍历 map 时,不保证每次执行程序获取的元素顺序相同。

    删除map中的key

    删除 mapkey 的语法是 delete(map, key)。这个函数没有返回值。

    package main
    
    import (  
        "fmt"
    )
    
    func main() {  
        personSalary := map[string]int{
            "steve": 12000,
            "jamie": 15000,
        }
        personSalary["mike"] = 9000
        fmt.Println("map before deletion", personSalary)
        delete(personSalary, "steve")
        fmt.Println("map after deletion", personSalary)
    
    }
    
    获取map的长度

    使用len()函数获取map的长度

    len(personSalary)
    
    
    注意:

    Map是引用类型,和 slices 类似。当 map 被赋值为一个新变量的时候,它们指向同一个内部数据结构。因此,改变其中一个变量,就会影响到另一变量。

    当 map 作为函数参数传递时也会发生同样的情况。函数中对 map 的任何修改,对于外部的调用都是可见的。

    map 之间不能使用 == 操作符判断,== 只能用来检查 map 是否为 nil。会报错: invalid operation: map1 == map2 (map can only be compared to nil)。

    如果文章对您有帮助,记得点个赞,后续持续更新ing~~~

    相关文章

      网友评论

        本文标题:Go基础语法(四)

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