• golang语法要点笔记


    golang学习笔记

    读《go学习笔记第四版》 《学习go语言》 《gopl-zh》《Go语言实战》记录

     

    多变量赋值时,先计算所有相关值,然后再从左到右依次赋值。

    data, i := [3]int{1, 2, 3}, 0
    i, data[i] = 2, 6
    fmt.Println(i)  //2
    fmt.Println(data)  //[6 2 3]

    用{}区分代码块

    常量值必须是编译期可确定的数字、字符串、布尔值。 未使用局部常量不会引发编译错误。 

    const (    
        _ = iota                  // iota = 0
        KB int64 = 1 << (10 * iota)      // iota = 1   
        MB                          // 与 KB 表达式相同,但 iota = 2
        GB
        TB
     )
    const (
        a = iota
        b = iota
        c = iota
    )

    可以简写

    const (
        a = iota
        b
        c
    )
     const (
                a = iota   //0
                b          //1
                c          //2
                d = "ha"   //独立值,iota += 1
                e          //"ha"   iota += 1
                f = 100    //iota +=1
                g          //100  iota +=1
                h = iota   //7,恢复计数
                i          //8
        )
        fmt.Println(a,b,c,d,e,f,g,h,i)
    // 0 1 2 ha ha 100 100 7 8
    const (
        i=1<<iota
        j=3<<iota
        k
        l
    )
    
    func main() {
        fmt.Println("i=",i)
        fmt.Println("j=",j)
        fmt.Println("k=",k)
        fmt.Println("l=",l)
    }
    //output:
    i= 1
    j= 6
    k= 12
    l= 24
    var s []int    // len(s) == 0, s == nil
    s = nil        // len(s) == 0, s == nil
    s = []int(nil) // len(s) == 0, s == nil
    s = []int{}    // len(s) == 0, s != nil

      

    map中的元素并不是一个变量,不能对map的元素进行取址操作

     range 会复制对象

    a := [3]int{0, 1, 2}
    for i, v := range a {    // index、value 都是从复制品中取出。
        if i == 0 {    // 在修改前,我们先修改原数组。
        a[1], a[2] = 999, 999
         fmt.Println(a)    // 确认修改有效,输出 [0, 999, 999]。
        }
        a[i] = v + 100    // 使⽤用复制品中取出的 value 修改原数组。 
    }
    fmt.Println(a)                    // 输出 [100, 101, 102]。
    s := []int{1, 2, 3, 4, 5}
    for i := range s {       // 复制 struct slice { pointer, len, cap }。
        if i == 0 {
         s = s[:3]           // 对 slice 的修改,不会影响 range。 
        s[2] = 100          // 对底层数据的修改。    
        }
    }
    fmt.Println(s)  //[1 2 100 4 5]
    //range也可以用来枚举Unicode字符串。第一个参数是字符的索引,第二个是字符(Unicode的值)本身。
    for i, c := range "go" {
        fmt.Println(i, c)
    }
    var employeeOfTheMonth *Employee = &dilbert
    employeeOfTheMonth.Position += " (proactive team player)"

    相当于

    (*employeeOfTheMonth).Position += " (proactive team player)"


    var c = [...]int {1, 2, 3, 4, 5} //由初始化列表决定数组长度,不可省去标识符 "...",否则将变成切片Slice


        x := []int{1, 2, 3}
        i := 2
        switch i {
        case x[1]:
            println("a")
        case 1, 3:    //同时匹配1,3
            println("b")
        default:
            println("c")
        }
    switch i := x[2]; {           // 带初始化语句
        case i > 0:
            println("a")
        case i < 0:
            println("b")
        default:
            println("c")
    }

    MAP

      /* map插入key - value对,各个国家对应的首都 */
        countryCapitalMap [ "France" ] = "Paris"
        countryCapitalMap [ "Italy" ] = "罗马"
        countryCapitalMap [ "Japan" ] = "东京"
        countryCapitalMap [ "India " ] = "新德里"
    
        /*使用键输出地图值 */ for country := range countryCapitalMap {
            fmt.Println(country, "首都是", countryCapitalMap [country])
        }
    
        /*查看元素在集合中是否存在 */
        captial, ok := countryCapitalMap [ "美国" ] /*如果确定是真实的,则存在,否则不存在 */
        /*fmt.Println(captial) */
        /*fmt.Println(ok) */
        if (ok) {
            fmt.Println("美国的首都是", captial)
        } else {
            fmt.Println("美国的首都不存在")
        }

    break 可用于 for、switch、select,而 continue 仅能用于 for 循环。

    type User struct {
        id   int
        name string
    }
    
    func (self *User) Test() {
        fmt.Printf("%p, %v
    ", self, self)
    }
    func main() {
        u := User{1, "Tom"}
        u.Test()
        mValue := u.Test
        mValue() // 隐式传递 receiver
        mExpression := (*User).Test
        mExpression(&u) // 显式传递 receiver
    }
    /*
    0x210230000, &{1 Tom}
    0x210230000, &{1 Tom}
    0x210230000, &{1 Tom}
    */
    type User struct {
        id   int
        name string
    }
    
    func (self User) Test() {
        fmt.Println(self)
    }
    func main() {
        u := User{1, "Tom"}
        mValue := u.Test // ⽴立即复制 receiver,因为不是指针类型,不受后续修改影响。
        u.id, u.name = 2, "Jack"
        u.Test()
        mValue()
    }
    /*
    {2 Jack} 
    {1 Tom}
    */
    func (self *User) TestPointer() {
        fmt.Printf("TestPointer: %p, %v
    ", self, self)
    }
    func (self User) TestValue() {
        fmt.Printf("TestValue: %p, %v
    ", &self, self)
    }
    func main() {
        u := User{1, "Tom"}
        fmt.Printf("User: %p, %v
    ", &u, u)
        mv := User.TestValue
        mv(u)
        mp := (*User).TestPointer
        mp(&u)
        mp2 := (*User).TestValue // *User ⽅方法集包含 TestValue。
        mp2(&u)                  // 签名变为 func TestValue(self *User)。
    } // 实际依然是 receiver value copy
    /*
    User       : 0x210231000, {1 Tom}
    TestValue  : 0x210231060, {1 Tom}
    TestPointer: 0x210231000, &{1 Tom}
    TestValue  : 0x2102310c0, {1 Tom}
    */
    type Data struct{}
    
    func (Data) TestValue()    {}
    func (*Data) TestPointer() {}
    func main() {
        var p *Data = nil
        p.TestPointer()
        (*Data)(nil).TestPointer() // method value
        (*Data).TestPointer(nil)   // method expression
        //p.TestValue()    // invalid memory address or nil pointer dereference
        // (Data)(nil).TestValue()  // cannot convert nil to type Data    
        // Data.TestValue(nil)      // cannot use nil as type Data in function argument 
    }

    简短变量声明左边的变量可能并不是全部都是刚刚声明的。如 果有一些已经在相同的词法域声明过了,那么简短变量声明语句对这些已经声明过 的变量就只有赋值行为了,简短变量声明语句中必须至少要声明一个新的变量。如果变量 是在外部词法域声明的,那么简短变量声明语句将会在当前词法域重新声明一个新的变量

    //交换两个元素
    x, y = y, x
    a[i], a[j] = a[j], a[i]
    //获得整数对应二进制数1的个数
    var pc [256]byte
    
    func init() {
        for i := range pc {
            pc[i] = pc[i/2] + byte(i&1)
        }
        fmt.Printf("%b", pc)
    }
    
    func PopCount(x uint64) int {
        return int(pc[byte(x>>(0*8))] +
            pc[byte(x>>(1*8))] +
            pc[byte(x>>(2*8))] +
            pc[byte(x>>(3*8))] +
            pc[byte(x>>(4*8))] +
            pc[byte(x>>(5*8))] +
            pc[byte(x>>(6*8))] +
            pc[byte(x>>(7*8))])
    }

    表达式 x&(x-1) 用于将x的最低的一个非零的bit位清零。已用来计算二进制数1的个数

    作用域

        if x := f(); x == 0 {
            fmt.Println(x)
        } else if y := g(x); x == y {
            fmt.Println(x, y)
        } else {
            fmt.Println(x, y)
        }
        fmt.Println(x, y) //    compile    error:    x    and    y    are    not    visible    here
    var cwd string
    func init() {
        cwd, err := os.Getwd() //    compile    error:    unused:    cwd        
        if err != nil {
            log.Fatalf("os.Getwd    failed:    %v", err)
        }
    }

    虽然cwd在外部已经声明过,但是 := 语句还是将cwd和err重新声明为新的局部变量。因为内 部声明的cwd将屏蔽外部的声明,因此上面的代码并不会正确更新包级声明的cwd变量。

    最直接的方法是通过单独声明err变量,来避免使 用 := 的简短声明方式:

    var cwd string
    func init() {
        var err error
        cwd, err = os.Getwd()
        if err != nil {
            log.Fatalf("os.Getwd    failed:    %v", err)
        }
    }

    运算符号:

    在Go语言中,%取模运算 符的符号和被取模数的符号总是一致的,因此 -5%3 和 -5%-3 结果都是-2。除法运算符 / 的 行为则依赖于操作数是否为全为整数,比如 5.0/4.0 的结果是1.25,但是5/4的结果是1,因为 整数除法会向着0方向截断余数。

     位运算:

    ^     位运算   XOR //只有一个1时返回1
    &^    位清空   (AND NOT)

    位操作运算符 ^ 作为二元运算符时是按位异或(XOR),当用作一元运算符时表示按位取反

    结构

    点操作符也可以和指向结构体的指针一起工作:

    var employeeOfTheMonth *Employee = &dilbert
    employeeOfTheMonth.Position += " (proactive team player)"

    相当于

    (*employeeOfTheMonth).Position += " (proactive team    player)"
    dilbert.Position = "hello" 相当于 (&dilbert).Position = "hello2"
    pp := &Point{1, 2}
    相当于
    pp := new(Point)
    *pp = Point{1, 2}
    w = Wheel{Circle{Point{8, 8}, 5}, 20}
    等价于
    w = Wheel{
        Circle: Circle{
            Point: Point{X:    8, Y: 8},
            Radius: 5,
        },    
        Spokes: 20,    //NOTE: trailing comma necessary here (and    at    Radius) 
    }      
    // squares返回一个匿名函数。
    // 该匿名函数每次被调用时都会返回下一个数的平方。
    func squares() func() int {
        var x int
        return func() int {
            x++
            return x * x
        }
    }
    func main() {
        f := squares()
        fmt.Println(f()) // "1"
        fmt.Println(f()) // "4"
        fmt.Println(f()) // "9"
        fmt.Println(f()) // "16"
    }

    结构

    var (
        mu sync.Mutex // guards mapping
        mapping = make(map[string]string)
    )
    
    func Lookup(key string) string {
        mu.Lock()
        v := mapping[key]
        mu.Unlock()
        return v
    }

    下面这个版本在功能上是一致的,但将两个包级别的变量放在了cache这个struct一组内:

    var cache = struct {
        sync.Mutex
        mapping map[string]string
    }{
        mapping: make(map[string]string),
    }
    
    
    func Lookup(key string) string {
        cache.Lock()
        v := cache.mapping[key]
        cache.Unlock()
        return v
    }
    type Point struct{ X, Y float64 }
    
    func (p Point) Add(q Point) Point { return Point{p.X + q.X, p.Y + q.Y} }
    func (p Point) Sub(q Point) Point { return Point{p.X - q.X, p.Y - q.Y} }
    
    type Path []Point
    
    func (path Path) TranslateBy(offset Point, add bool) {
        var op func(p, q Point) Point
        if add {
            op = Point.Add
        } else {
            op = Point.Sub
        }
        for i := range path {
            // Call either path[i].Add(offset) or path[i].Sub(offset).
            path[i] = op(path[i], offset)
        }
    }

    deferred

    func ma() {
        fmt.Println("im ma")
    }
    
    func main() {
        defer ma()
        fmt.Println("start")
    }
    /*
    start
    im ma
    */
    func ma() func() {
        fmt.Println("im ma")
        return func() {
            fmt.Print("ma func end")
        }
    }
    
    func main() {
        defer ma()()
        fmt.Println("start")
    }
    /*
    im ma
    start
    ma func end
    */

    接口

    type IntSet struct { /* ... */ }
    func (*IntSet) String() string
    var _ = IntSet{}.String() // compile error: String requires *IntSet receiver

    但是我们可以在一个IntSet值上调用这个方法: var s IntSet var _ = s.String() // OK: s is a variable and &s has a String method

    然而,由于只有*IntSet类型有String方法,所以也只有*IntSet类型实现了fmt.Stringer接口: var _ fmt.Stringer = &s // OK var _ fmt.Stringer = s // compile error: IntSet lacks String method

    array := [5]*int{0: new(int), 1: new(int)}

    *array[0] = 10
    *array[1] = 20

    创建一个包含 100 万个 int 类型元素的数组

    var array [1e6]int

    nil slice 和 empty slice 区别 https://blog.csdn.net/bobodem/article/details/80658466

    nil slice

    var slice []int

    empty slice

    slice := make([]int,0)//或者

    slice := []int{}

      根据内存和性能来看,在函数间传递数组是一个开销很大的操作。在函数之间传递变量时,
    总是以值的方式传递的。如果这个变量是一个数组,意味着整个数组,不管有多长,都会完整复
    制,并传递给函数。

      在函数间传递切片就是要在函数间以值的方式传递切片。由于切片的尺寸很小,在函数间复
    制和传递切片成本也很低。由于与切片关联的数据包含在底层数组里,不属于切片本身,所以将切片

    复制到任意函数的时候,对底层数组大小都不会有影响。复制时只会复制切片本身,不会涉及底

    层数组。

      在函数间传递映射并不会制造出该映射的一个副本。实际上,当传递映射给一个函数,并对
    这个映射做了修改时,所有对这个映射的引用都会察觉到这个修改。这个特性和切片类似,保证可以用很小的成本来复制映射。

    func main() {
        slice := []int{10, 20, 30, 40, 50}
        newSliceA := slice[1:3:3]
        newSliceB := slice[1:3]
        newSliceA = append(newSliceA, 22)
        fmt.Println(slice)
        fmt.Println(newSliceA)
        newSliceB = append(newSliceB, 22)
        fmt.Println(slice)
        fmt.Println(newSliceA)
    }
    /*
    [10 20 30 40 50]
    [20 30 22]
    [10 20 30 22 50]
    [20 30 22]
     */
    func main() {
        slice := make([]int ,1E6)
        fmt.Println(len(slice))
        fmt.Println(cap(slice))
        slice = append(slice, 99)
        fmt.Println(len(slice))
        fmt.Println(cap(slice))
        /**
    1000000
    1000000
    1000001
    1250304
         */
    }
    Go 语言里的引用类型有如下几个:切片、映射、通道、接口和函数类型

    break

    //利用 break 可以提前退出循环,break 终止当前的循环。
    fo r i := 0 ; i < 10 ; i++ {
        i f i > 5 {
        break ← 终止这个循环,只打印 05 }
        println(i)
    }
    
    循环嵌套循环时,可以在 break 后指定标签。用标签决定哪个循环被终止:
    
    J: for j := 0 ; j < 5 ; j++ {
        for i := 0 ; i < 10 ; i++ {
        if i > 5 {    
          break J ← 现在终止的是 j 循环,而不是 i 的那个
        }
       println(i)
       } 
    }

    Switch

    //Go 的 switch 非常灵活。表达式不必是常量或整数,执行的过程从上至//下,直到找到匹
    //配项,而如果 switch 没有表达式,它会匹配 true 。这产生一种可能——//使用 switch
    //编写 if-else-if-else 判断序列。
    func unhex(c byte) byte {
        switch {
        case '0' <= c && c <= '9':
          return c - '0'
        case 'a' <= c && c <= 'f':
          return c - 'a' + 10
        case 'A' <= c && c <= 'F':
          return c - 'A' + 10
    }
      return 0 
    } //它不会匹配失败后自动向下尝试,但是可以使用 fallthrough 使其这样做。没有 fallthrough:
    switch i { case 0: // 空的 case 体 case 1: f() // 当 i == 0 时,f 不会被调用! } 而这样: switch i { case 0: fallthrough case 1: f() // 当 i == 0 时,f 会被调用! } //用 default 可以指定当其他所有分支都不匹配的时候的行为。 switch i {   case 0:   case 1:     f()   defaul t:     g() // 当 i 不等于 0 或 1 时调用 } //分支可以使用逗号分隔的列表。 func shouldEscape(c byte) bool {   switch c {     case ' ', '?', '&', '=', '#', '+': ← , as ”or”     return true   }   return false }

    Slice

    slice 总是指向底层的一个 array。slice 是一个指向 array 的指针,这是其与 array 不同的地方;slice 是引用
    类型,这意味着当赋值某个 slice 到另外一个变量,两个引用会指向同一个 array。例 引 用 类 型 使
    如,如果一个函数需要一个 slice 参数,在其内对 slice 元素的修改也会体现在函数调 用 make 创建。
    用者中,这和传递底层的 array 指针类似
     
        s1 := []int{1, 2, 3}
        s2 := s1
        s1[1] = 4
        fmt.Println(s1, s2) //[1 4 3] [1 4 3]
    
        d := func (s1 []int) {
            s1[2] = 5
        }
        d(s1)
        fmt.Println(s1, s2)    //[1 4 5] [1 4 5]

    闭包

    func plusX(x int) func(int) int { .0
        return func(y int) int { retu rn x + y } .1 
    }
     .0 再次定义一个函数返回一个函数;
    .1 在函数符号中使用局部变量 x。
     

    defer

     

    //在这个(匿名)函数中,可以访问任何命名返回参数:
    //Listing 2.10. 在 defer 中访问返回值
    func f() (ret i n t) { ← ret 初始化为零
        defer func() {
        ret++ ← ret 增加为 1 }()
        return 0 ← 返回的是 1 而不是 0! 
    }

     

     
  • 相关阅读:
    隐式图回溯法之八皇后问题解答
    试用O(n)来实现求出一个无序数组的中位数
    C++学习第一弹: const 指针 引用答疑
    一道面试题的解答_骑士获得金币问题
    根据已知词表选出好词(直通车)
    python3.5爬虫完成笔趣阁小说的爬取
    关于_水木社区[日经题]_10只狗鉴别1000瓶中哪一瓶药有毒_的解答思路
    数据库想法整合,多表联立交互
    [网络推广]直通车学习
    3行实现模糊匹配
  • 原文地址:https://www.cnblogs.com/8000cabbage/p/8453815.html
Copyright © 2020-2023  润新知