一尘不染

Golang append()何时创建新切片?

go

根据内置的api docs,当原始切片的容量不够大时,append()将重新分配并复制到新的数组块。

这是递归算法(的简化版本),用于创建字母(在这种情况下为布尔值)的组合。字母表中的成员(真,假)将递归地添加到切片中,直到其长度正确为止,然后通过通道将其发送出去。

package main

import (
    "fmt"
)

func AddOption(c chan []bool, combo []bool, length int) {
    if length == 0 {
        fmt.Println(combo, "!")
        c <- combo
        return
    }
    var newCombo []bool
    for _, ch := range []bool{true, false} {
        newCombo = append(combo, ch)
        AddOption(c, newCombo, length-1)
    }
}

func main() {
    c := make(chan []bool)
    go func(c chan []bool) {
        defer close(c)
        AddOption(c, []bool{}, 4)
    }(c)
    for combination := range c {
        fmt.Println(combination)
    }
}

是此代码的操场链接。在输出中:

[true true true true] !
[true true true false] !
[true true true false]
[true true true false]
[true true false true] !
[true true false false] !
[true true false false]
[true true false false]
[true false true true] !
[true false true false] !
[true false true false]
[true false true false]
[true false false true] !
[true false false false] !
[true false false false]
[true false false false]
[false true true true] !
[false true true false] !
[false true true false]
[false true true false]
[false true false true] !
[false true false false] !
[false true false false]
[false true false false]
[false false true true] !
[false false true false] !
[false false true false]
[false false true false]
[false false false true] !
[false false false false] !
[false false false false]
[false false false false]

以感叹号结尾的行是从AddOption发送到通道的行。那些没有的东西则出现在另一端(即main()中)。显然,通过通道发送的切片在发送后会更改。

由于AddOption在发送切片后立即返回,因此修改必须来自代码块

var newCombo []bool
for _, ch := range []bool{true, false} {
    newCombo = append(combo, ch)
    AddOption(c, newCombo, length-1)
}

但是,根据文档,append()应该返回一个新的切片(cap(combo)不够大)。根据这个答案,发送给AddOption的slice描述符应该是一个副本。那不是真的吗?据我所知,作为AddOption()的第二个参数发送的值要么是指向切片描述符的指针,要么append()没有返回新的切片。


阅读 251

收藏
2020-07-02

共1个答案

一尘不染

append()创建一个新的片,它不会创建片,这只是一个比以前更大片。实际上,它创建的切片已经比上一个元素大几个元素。看一下这段代码:

package main

import "fmt"

func main() {
    var sl []bool

    for i := 0; i < 100; i++ {
        sl = append(sl, true)
        fmt.Println(cap(sl))
    }
}

Playground

如果运行此代码,则会看到容量最初在每次分配时都会增加一倍。对于较大的切片大小,当然会更改此策略。

2020-07-02