2016-03-10 22 views
1

我怎样才能遍历切片,并通过除当前元素之外的切片?似乎append()函数修改底层切片,我们可以在文档中看到。但无论如何,我仍然不知道如何达到这一点。为什么追加修改通过切片

func main() { 
    args := []string{ "2", "3", "8" } 

    for i, _ := range args { 
     fmt.Println(append(args[:i], args[i+1:]...)) // or pass to function 
    } 

    fmt.Println(args) 
} 

结果:

[3 8] 
[3 8] 
[3 8] 
[3 8 8] // it is args now 

我的期望:

[3 8] 
[2 8] 
[2 3] 

我已经看到了这Why does append() modify the provided slice? (See example)

,但什么是切片的容量是秘密我,我不明白我为什么超过它。

+2

如果你不希望修改一个切片,你需要的值复制到新的切片。除此之外,我不太了解你的问题。你能展示你期望发生的事情吗? – JimB

+0

JimB,我修改了这个问题,这样你就可以看到我期望的 –

回答

1

您可以将切片想象成一个由固定大小的数组和一个计数器组成的结构体。切片的容量是底层数组的大小,而切片的长度是计数器。

追加定义那样:func append(slice []Type, elems ...Type) []Typegodoc),这基本上意味着你将追加elem可变参数参数的slice说法。如果len(elems) + len(slice) > cap(slice),那么udnerlying数组需要更大一个(具有更大的容量),这意味着(在去)一个新的切片(因此返回参数)。

就你而言,你没有超过切片的容量。你只是修改了它的内容。

一个简单的(虽然略有丑陋的)技巧将两个嵌套追加到一个空切片:

package main 

import "fmt" 

func main() { 
    args := []string{ "2", "3", "8" } 

    for i, _ := range args { 
     fmt.Println(append(append([]string{}, args[:i]...), args[i+1:]...)) 
    } 

    fmt.Println(args) 
} 

或者,如果你想切片的副本传递给方法(和做什么你想要之后),您可以使用copy函数...

1

追加总是尝试修改底层数组。

让我们看一下环

append(args[:0], args[0+1:]...) 

这样做是切片{3,8}附加到切片{}因为args来第一次执行[:0]为您提供了一个空的切片从而结束在阵列的开始。这就是为什么你的数组出现为[3 8 8],因为3 8被附加到数组中。 了解更多关于on the wiki

可以使用使设置默认容量即

args := make([]string, 0, CAPACITY) 

您还可以检查容量片

a := []int{1,2,3} 
fmt.Println(cap(a)) 
>>> 3 

最后,如果你不希望每次都以重新复制阵列在Elwinar的答案中,我会建议将两个片段a [:i]和一个[i + 1:]传递给函数。

1

表现是很大的原因。创建新切片并将所有元素复制到切片上会很昂贵,所以切片代码不会没有充分的理由进行复制。但是,如果超过切片的容量,则通过复制底层切片来增加适当的量。这意味着,这是一个从append返回的片可能不是你传入的同一片

使用的首选方式是:

args = append(args, newarg) 

如果拿子切片,容量保持不变,但您观察切片变化。这意味着缺失的元素仍然存在,但超出了新片的范围。

这解释了您的代码的奇怪输出。您每次打印append的结果,但不存储该结果,这意味着args与您打印的内容不一样。

最初的args切片是3大元素。对于每个索引i - 也就是说对于0,12 - 您需要一个子索引args[:i],并将数组args[i+1:]的其余部分的所有元素附加到它。这意味着:

i args[:i]  args[i+1]... Result   args 
0 {}   {"3", "8"}  {"3", "8"}  {"3", "8", "8"} 
1 {"3"}  {"8"}   {"3", "8"}  {"3", "8", "8"} 
2 {"3", "8"} {}    {"3", "8"}  {"3", "8", "8"} 

TL;博士您应始终保存的append的结果,如果你想打副本,以便你可以改变它,然后自己进行复印。

1

The Go Programming Language Specification

Appending to and copying slices

内置的功能追加和复制协助共同切片 操作。对于这两个函数,结果与参数引用的内存是否重叠无关。

的可变参数函数追加追加零个或多个值x为s S型的,它必须是一个切片类型,并返回生成的切片,也 型S的值x被传递给类型的参数... T 其中,T是S的元素类型,并且适用通过规则 的各个参数。作为一种特殊情况,append还接受第一个参数 ,该参数可分配给类型为[]的字节,第二个参数为字符串类型 ,后跟....此表格附加字符串的字节。

append(s S, x ...T) S // T is the element type of S 

如果s的容量不够大,以适应附加的值, 追加分配适合 现有的片元件和所述附加的值既一个新的,足够大的底层数组。否则, 追加重新使用底层数组。

s0 := []int{0, 0} 
s1 := append(s0, 2)    // append a single element  s1 == []int{0, 0, 2} 
s2 := append(s1, 3, 5, 7)   // append multiple elements s2 == []int{0, 0, 2, 3, 5, 7} 
s3 := append(s2, s0...)   // append a slice    s3 == []int{0, 0, 2, 3, 5, 7, 0, 0} 
s4 := append(s3[3:6], s3[2:]...) // append overlapping slice s4 == []int{3, 5, 7, 2, 3, 5, 7, 0, 0} 

var t []interface{} 
t = append(t, 42, 3.1415, "foo") //        t == []interface{}{42, 3.1415, "foo"} 

var b []byte 
b = append(b, "bar"...)   // append string contents  b == []byte{'b', 'a', 'r' } 

功能拷贝拷贝切片从源SRC为 目的地DST元素,并返回复制的元素的数量。两个参数 必须具有相同的元素类型T,并且必须可分配给 类型为[] T的片段。复制元素的数量是最小的 len(src)和len(dst)。作为一种特殊情况,copy还接受一个 目标参数,该参数可分配给类型为[]字节的字符串类型的源参数 。该表格将字符串中的字节复制到 字节片段中。

copy(dst, src []T) int 
copy(dst []byte, src string) int 

例子:

var a = [...]int{0, 1, 2, 3, 4, 5, 6, 7} 
var s = make([]int, 6) 
var b = make([]byte, 5) 
n1 := copy(s, a[0:])   // n1 == 6, s == []int{0, 1, 2, 3, 4, 5} 
n2 := copy(s, s[2:])   // n2 == 4, s == []int{2, 3, 4, 5, 4, 5} 
n3 := copy(b, "Hello, World!") // n3 == 5, b == []byte("Hello") 

不要使用追加覆盖您的输入。为你的输出使用一个单独的变量(函数参数)。例如,

package main 

import "fmt" 

func main() { 
    args := []string{"2", "3", "8"} 
    fmt.Println(args) 
    funcArg := make([]string, len(args)-1) 
    for i := range args { 
     copy(funcArg, args[:i]) 
     copy(funcArg[i:], args[i+1:]) 
     fmt.Println(funcArg) 
    } 
    fmt.Println(args) 
} 

输出:

[2 3 8] 
[3 8] 
[2 8] 
[2 3] 
[2 3 8]