切片-学习Go语言

阅读笔记

slices.go

package main

import "fmt"

func main() {
    primes := [6]int{2, 3, 5, 7, 11, 13}

    var s []int = primes[1:4]
    fmt.Println(s)
}

笔记

切片的内部实现,类似一个链表+头尾指针。

数组的切片范围为low<= x < high

slices-pointers.go

package main

import "fmt"

func main() {
    names := [4]string{
        "John",
        "Paul",
        "George",
        "Ringo",
    }
    fmt.Println(names)

    a := names[0:2]
    b := names[1:3]
    fmt.Println(a, b)

    b[0] = "XXX"
    fmt.Println(a, b)
    fmt.Println(names)
}

笔记

切片的数据被引用的。修改切片的数据会直接关联到原来的数组。

slice-literals.go

package main

import "fmt"

func main() {
    q := []int{2, 3, 5, 7, 11, 13}
    fmt.Println(q)

    r := []bool{true, false, true, true, false, true}
    fmt.Println(r)

    s := []struct {
        i int
        b bool
    }{
        {2, true},
        {3, false},
        {5, true},
        {7, true},
        {11, false},
        {13, true},
    }
    fmt.Println(s)
}

笔记

  1. 创建长度为6的int数组,构建一个引用该数组的切片。
  2. 创建长度为6的布尔数组,构建一个引用该数组的切片。
  3. 创建长度为5的结构体数组,构建一个引用该数组的切片。

slice-bounds.go

package main

import "fmt"

func main() {
    s := []int{2, 3, 5, 7, 11, 13}

    s = s[1:4]
    fmt.Println(s)

    s = s[:2]
    fmt.Println(s)

    s = s[1:]
    fmt.Println(s)
}

笔记

  1. 切片的默认值为0到数组长度。
  2. 以数组var a [10]int为例
  3. 切片等价a[0:10] = a[:10] = a[0:] = a[:]

slice-len-cap.go

package main

import "fmt"

func main() {
    s := []int{2, 3, 5, 7, 11, 13}
    printSlice(s)

    // Slice the slice to give it zero length.
    s = s[:0]
    printSlice(s)

    // Extend its length.
    s = s[:4]
    printSlice(s)

    // Drop its first two values.
    s = s[2:]
    printSlice(s)
}

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

笔记

  1. 如何理解切片的长度和容量。按照正常逻辑不是只有长度就够了吗?为啥还要新增一个容量呢?
  2. 切片的长度就是它所包含的元素个数。
  3. 切片的容量是从它的第一个元素开始数,到其底层数组元素末尾的个数。
  4. 注意看第二个例子,长度变为0 ,但是容量为什么还是6呢?
  5. 注意第四个例子,是不是起始值变了,才会去修改引用数组的容量?

nil-slices.go

package main

import "fmt"

func main() {
    var s []int
    fmt.Println(s, len(s), cap(s))
    if s == nil {
        fmt.Println("nil!")
    }
}

笔记

  1. 切片的零值是 nil
  2. nil 切片的长度和容量为 0 且没有底层数组。

making-slices.go

package main

import "fmt"

func main() {
    a := make([]int, 5)
    printSlice("a", a)

    b := make([]int, 0, 5)
    printSlice("b", b)

    c := b[:2]
    printSlice("c", c)

    d := c[2:5]
    printSlice("d", d)
}

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

笔记

  1. 默认创建len 和cap 相同的切片
  2. a := make([]int, 5) // len(a)=5 cap(a)=5
  3. 注意第四个例子,cap变成了3
  4. 假如d1 := c[3:5],那么cap会变成2
  5. 切片的最大值不能超过原始数组的长度,不然会报错。

slices-of-slice.go

package main

import (
    "fmt"
    "strings"
)

func main() {
    // Create a tic-tac-toe board.
    board := [][]string{
        []string{"_", "_", "_"},
        []string{"_", "_", "_"},
        []string{"_", "_", "_"},
    }

    // The players take turns.
    board[0][0] = "X"
    board[2][2] = "O"
    board[1][2] = "X"
    board[1][0] = "O"
    board[0][2] = "X"

    for i := 0; i < len(board); i++ {
        fmt.Printf("%s\n", strings.Join(board[i], " "))
    }
}

笔记

  1. 这尼玛不是就二维数组了吗?
  2. 创建了一个二维数组的切片,也就是切片的切片

append.go

package main

import "fmt"

func main() {
    var s []int
    printSlice(s)

    // append works on nil slices.
    s = append(s, 0)
    printSlice(s)

    // The slice grows as needed.
    s = append(s, 1)
    printSlice(s)

    // We can add more than one element at a time.
    s = append(s, 2, 3, 4)
    printSlice(s)
}

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

笔记

  1. func append(s []T, vs …T) []T 函数原型

  2. 注意第三次追加,cap自动扩充到2^ 3 = 8

  3. 那如果再超过当前的cap呢

  4. 比如 s = append(s, 5,6,7,8),cap会自动扩容到2^4 = 16

  5. len=9 cap=16 [0 1 2 3 4 5 6 7 8]
    
  6. 这难道底层是采用hash+链表实现的吗?还有自动扩容这一说?

  7. Go 切片:用法和本质

range.go

package main

import "fmt"

var pow = []int{1, 2, 4, 8, 16, 32, 64, 128}

func main() {
    for i, v := range pow {
        fmt.Printf("2**%d = %d\n", i, v)
    }
}

笔记

  1. 这不就是PHP数组的遍历,键值对吗?

rang-continued.go

package main

import "fmt"

func main() {
    pow := make([]int, 10)
    for i := range pow {
        pow[i] = 1 << uint(i) // == 2**i
    }
    for _, value := range pow {
        fmt.Printf("%d\n", value)
    }
    for value := range pow {
        fmt.Printf("%d\n", value)
    }
    for key, _ := range pow {
        fmt.Printf("%d\n", key)
    }
}

笔记

  1. 输出键值对的方法。

思考总结

操作数组切片的方法。
1. make
2. append

参考链接

  1. 切片
  2. 切片就像数组的引用
  3. 切片文法
  4. 切片的默认行为
  5. 切片的长度与容量
  6. nil 切片
  7. 用 make 创建切片
  8. 切片的切片
  9. 向切片追加元素
  10. Range
  11. Range续

结构体-学习Go语言

阅读笔记

pointers.go

package main

import "fmt"

func main() {
    i, j := 42, 2701

    p := &i         // point to i
    fmt.Println(*p) // read i through the pointer
    *p = 21         // set i through the pointer
    fmt.Println(i)  // see the new value of i

    p = &j         // point to j
    *p = *p / 37   // divide j through the pointer
    fmt.Println(j) // see the new value of j
}

笔记

Go 拥有指针。指针保存了值的内存地址。

类型 *T 是指向 T 类型值的指针。其零值为 nil。
& 操作符会生成一个指向其操作数的指针。
这也就是通常所说的“间接引用”或“重定向”。

structs.go

package main

import "fmt"

type Vertex struct {
    X int
    Y int
}

func main() {
    fmt.Println(Vertex{1, 2})
}

笔记

  1. 这个不是c语言的结构体是一样的吗?

struct-fields.go

package main

import "fmt"

type Vertex struct {
    X int
    Y int
}

func main() {
    v := Vertex{1, 2}
    v.X = 4
    fmt.Println(v)
}

笔记

  1. 内部实现是否类似c语言的指针?

struct-pointers.go

package main

import "fmt"

type Vertex struct {
    X int
    Y int
}

func main() {
    v := Vertex{1, 2}
    p := &v
    p.X = 1e9
    fmt.Println(v)
}

笔记

  1. v 和 p 都是指向结构体的指针吧
  2. p使用隐式间接引用

struct-literals.go

package main

import "fmt"

type Vertex struct {
    X, Y int
}

var (
    v1 = Vertex{1, 2}  // has type Vertex
    v2 = Vertex{X: 1}  // Y:0 is implicit
    v3 = Vertex{}      // X:0 and Y:0
    p  = &Vertex{1, 2} // has type *Vertex
)

func main() {
    fmt.Println(v1, p, v2, v3)
}

笔记

  1. 结构体会初始化字段的默认值
  2. 特殊的前缀 & 返回一个指向结构体的指针。

参考链接

  1. 指针
  2. 结构体
  3. 结构体字段
  4. 结构体指针
  5. 结构体文法

控制流程-学习Go语言

阅读笔记

if-with-a-short-statement.go

package main

import (
    "fmt"
    "math"
)

func pow(x, n, lim float64) float64 {
    if v := math.Pow(x, n); v < lim {
        return v
    }
    return lim
}

func main() {
    fmt.Println(
        pow(3, 2, 10),
        pow(3, 3, 20),
    )
}

笔记

同 for 一样, if 语句可以在条件表达式前执行一个简单的语句。

该语句声明的变量作用域仅在 if 之内。

(在最后的 return 语句处使用 v 看看。)

1. 这个特性很好。
2. if 的条件表达类似于PHP的表达式。
   if ( v = math.Pow(x,n) && v < lim){}

参考链接

  1. if
  2. if 的简短语句
  3. if 和 else