关于附加内置函数的一些问题
some question about append built-in function
package main
import (
"fmt"
)
func main() {
result := subsets([]int{9,3,0,1,2})
fmt.Println(result)
}
func subsets(nums []int) [][]int {
result := [][]int{}
var fun func([]int, int)
fun = func (preSets []int, start int) {
// fmt.Println(start, preSets, result)
result = append(result, preSets)
// fmt.Println(result)
for idx := start; idx < len(nums); idx++ {
tmp := nums[idx]
newSet := append(preSets, tmp)
fun(newSet, idx+1)
newSet = newSet[:len(newSet) - 1]
}
}
fun([]int{}, 0)
return result
}
我想找到切片的子集,并且认为上面的代码应该可以工作。但它给了我以下输出
[[] [9] [9 3] [9 3 0] [9 3 0 2] [9 3 0 1 2] [9 3 0 2] [9 3 1] [9 3 1 2] [9 3 2] [9 0] [9 0 1] [9 0 1 2] [9 0 2] [9 1] [9 1 2] [9 2] [3] [3 0] [3 0 1] [3 0 1 2] [3 0 2] [3 1] [3 1 2] [3 2] [0] [0 1] [0 1 2] [0 2] [1] [1 2] [2]]
第五片应该是[9 3 0 1],但它是[9 3 0 2],我每一步打印结果,我发现第五片从[9301]变成了[9302] ] 第7个slice[9302]追加的时候,我觉得应该和slice下的数组存储有关,但是为什么
i think [the problem is] related to the array storage under the slice
是的,是的。
but why
append
函数:
- 有时 re-uses 原始后备数组,但是
- 有时会创建一个新的支持数组并将原始值复制到新数组。
当它执行这两项中的一项时,没有做出任何承诺。
当它 re-uses 原始数组时,您会遇到您不喜欢的行为。当它创建一个新数组时,您会遇到您想要的行为。由于您想要复制行为,您可以简单地编写自己的“始终复制”代码并获得您想要的行为。但是,more-minimal 更改是替换:
result = append(result, preSets)
与:
result = append(result, unshare(preSets))
函数 unshared
定义为:
func unshare(a []int) []int {
tmp := make([]int, len(a))
copy(tmp, a)
return tmp
}
如果你想向自己解释 exactly 为什么你得到 exact 结果,这有点棘手,因为你'没有承诺什么时候 append
创建一个新的支持数组,什么时候 re-uses 现有的支持数组,但它 是 可以解释的,如果你做以下两个假设:
append
如果没有必要,从不复制支持数组;和
- 当
append
复制时,它有时会 over-allocates 新的支持数组。
也就是说,append
的行为或多或少是这样的,当然除了这个特定于 []int
:
func xappend(orig []int, add []int) []int {
has := len(orig)
needed := has + len(add)
// If we can fit the added elements in, do that now.
if cap(orig) >= needed {
fmt.Println("copy in place")
orig = orig[:needed]
copy(orig[has:needed], add)
return orig
}
newSize := undocumentedFunction(has, cap(orig), needed)
fmt.Println("make new, size", newSize)
new := make([]int, needed, newSize)
copy(new, orig)
copy(new[has:needed], add)
return new
}
func undocumentedFunction(oldCap, oldLen, newCap int) int {
twiceAsMuch := oldCap + oldCap
if newCap > twiceAsMuch {
return newCap
}
// 2x old capacity is enough, but
// we'll tweak this in various ways.
// The exact tweaks might change at any time.
if oldLen < 1024 {
return twiceAsMuch
}
panic("didn't copy this part")
}
这个“未记录的函数”是运行时系统的一部分,它与编译器协作。你可以看到它的实际代码 here (注意:这个 link 可能会在某些时候中断或走错行)。
(Go Playground 上的示例可运行代码 here。)
package main
import (
"fmt"
)
func main() {
result := subsets([]int{9,3,0,1,2})
fmt.Println(result)
}
func subsets(nums []int) [][]int {
result := [][]int{}
var fun func([]int, int)
fun = func (preSets []int, start int) {
// fmt.Println(start, preSets, result)
result = append(result, preSets)
// fmt.Println(result)
for idx := start; idx < len(nums); idx++ {
tmp := nums[idx]
newSet := append(preSets, tmp)
fun(newSet, idx+1)
newSet = newSet[:len(newSet) - 1]
}
}
fun([]int{}, 0)
return result
}
我想找到切片的子集,并且认为上面的代码应该可以工作。但它给了我以下输出
[[] [9] [9 3] [9 3 0] [9 3 0 2] [9 3 0 1 2] [9 3 0 2] [9 3 1] [9 3 1 2] [9 3 2] [9 0] [9 0 1] [9 0 1 2] [9 0 2] [9 1] [9 1 2] [9 2] [3] [3 0] [3 0 1] [3 0 1 2] [3 0 2] [3 1] [3 1 2] [3 2] [0] [0 1] [0 1 2] [0 2] [1] [1 2] [2]]
第五片应该是[9 3 0 1],但它是[9 3 0 2],我每一步打印结果,我发现第五片从[9301]变成了[9302] ] 第7个slice[9302]追加的时候,我觉得应该和slice下的数组存储有关,但是为什么
i think [the problem is] related to the array storage under the slice
是的,是的。
but why
append
函数:
- 有时 re-uses 原始后备数组,但是
- 有时会创建一个新的支持数组并将原始值复制到新数组。
当它执行这两项中的一项时,没有做出任何承诺。
当它 re-uses 原始数组时,您会遇到您不喜欢的行为。当它创建一个新数组时,您会遇到您想要的行为。由于您想要复制行为,您可以简单地编写自己的“始终复制”代码并获得您想要的行为。但是,more-minimal 更改是替换:
result = append(result, preSets)
与:
result = append(result, unshare(preSets))
函数 unshared
定义为:
func unshare(a []int) []int {
tmp := make([]int, len(a))
copy(tmp, a)
return tmp
}
如果你想向自己解释 exactly 为什么你得到 exact 结果,这有点棘手,因为你'没有承诺什么时候 append
创建一个新的支持数组,什么时候 re-uses 现有的支持数组,但它 是 可以解释的,如果你做以下两个假设:
append
如果没有必要,从不复制支持数组;和- 当
append
复制时,它有时会 over-allocates 新的支持数组。
也就是说,append
的行为或多或少是这样的,当然除了这个特定于 []int
:
func xappend(orig []int, add []int) []int {
has := len(orig)
needed := has + len(add)
// If we can fit the added elements in, do that now.
if cap(orig) >= needed {
fmt.Println("copy in place")
orig = orig[:needed]
copy(orig[has:needed], add)
return orig
}
newSize := undocumentedFunction(has, cap(orig), needed)
fmt.Println("make new, size", newSize)
new := make([]int, needed, newSize)
copy(new, orig)
copy(new[has:needed], add)
return new
}
func undocumentedFunction(oldCap, oldLen, newCap int) int {
twiceAsMuch := oldCap + oldCap
if newCap > twiceAsMuch {
return newCap
}
// 2x old capacity is enough, but
// we'll tweak this in various ways.
// The exact tweaks might change at any time.
if oldLen < 1024 {
return twiceAsMuch
}
panic("didn't copy this part")
}
这个“未记录的函数”是运行时系统的一部分,它与编译器协作。你可以看到它的实际代码 here (注意:这个 link 可能会在某些时候中断或走错行)。
(Go Playground 上的示例可运行代码 here。)