英文:
Concatenate two slices in Go
问题
我正在尝试将切片[1, 2]
和切片[3, 4]
合并。在Go中我应该如何做?
我尝试了:
append([]int{1,2}, []int{3,4})
但是得到了错误信息:
cannot use []int literal (type []int) as type int in append
然而,文档似乎表明这是可能的,我漏掉了什么?
slice = append(slice, anotherSlice...)
英文:
I'm trying to combine the slice [1, 2]
and the slice [3, 4]
. How can I do this in Go?
I tried:
append([]int{1,2}, []int{3,4})
but got:
cannot use []int literal (type []int) as type int in append
However, the documentation seems to indicate this is possible, what am I missing?
slice = append(slice, anotherSlice...)
答案1
得分: 1454
在第二个切片后面添加点号:
// vvv
append([]int{1,2}, []int{3,4}...)
这就像任何其他可变参数函数一样。
func foo(is ...int) {
for i := 0; i < len(is); i++ {
fmt.Println(is[i])
}
}
func main() {
foo([]int{9,8,7,6,5}...)
}
英文:
Add dots after the second slice:
// vvv
append([]int{1,2}, []int{3,4}...)
This is just like any other variadic function.
func foo(is ...int) {
for i := 0; i < len(is); i++ {
fmt.Println(is[i])
}
}
func main() {
foo([]int{9,8,7,6,5}...)
}
答案2
得分: 108
> 追加和复制切片
>
> 可变参数函数append
将零个或多个值x
追加到类型为S
的切片s
中,其中S
必须是切片类型,并返回结果切片,也是类型为S
的切片。值x
被传递给类型为...T
的参数,其中T
是S
的元素类型,并且适用相应的参数传递规则。作为特例,append
还接受可分配给类型[]byte
的第一个参数,其第二个参数为string
类型,后跟...
。此形式追加字符串的字节。
>
> append(s S, x ...T) S // T是S的元素类型
>
> s0 := []int{0, 0}
> s1 := append(s0, 2) // 追加单个元素 s1 == []int{0, 0, 2}
> s2 := append(s1, 3, 5, 7) // 追加多个元素 s2 == []int{0, 0, 2, 3, 5, 7}
> s3 := append(s2, s0...) // 追加切片 s3 == []int{0, 0, 2, 3, 5, 7, 0, 0}
>
> 传递参数给...参数
>
> 如果f
是具有最后一个参数类型为...T
的可变参数函数,则在函数内部,该参数等效于类型为[]T
的参数。在每次调用f
时,传递给最后一个参数的参数是类型为[]T
的新切片,其连续元素是实际参数,所有实际参数都必须可分配给类型T
。因此,切片的长度是绑定到最后一个参数的参数数量,并且可能因每个调用点而异。
你的问题的答案在Go编程语言规范的示例s3 := append(s2, s0...)
中。例如,
s := append([]int{1, 2}, []int{3, 4}...)
英文:
> Appending to and copying slices
>
> The variadic function append
appends zero or more values x
to s
> of type S
, which must be a slice type, and returns the resulting
> slice, also of type S
. The values x
are passed to a parameter of
> type ...T
where T
is the element type of S
and the respective
> parameter passing rules apply. As a special case, append also accepts
> a first argument assignable to type []byte
with a second argument of
> string
type followed by ...
. This form appends the bytes of the
> string.
>
> append(s S, x ...T) S // T is the element type of 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}
>
> Passing arguments to ... parameters
>
> If f
is variadic with final parameter type ...T
, then within the
> function the argument is equivalent to a parameter of type []T
. At
> each call of f
, the argument passed to the final parameter is a new
> slice of type []T
whose successive elements are the actual arguments,
> which all must be assignable to the type T
. The length of the slice is
> therefore the number of arguments bound to the final parameter and may
> differ for each call site.
The answer to your question is example s3 := append(s2, s0...)
in the Go Programming Language Specification. For example,
s := append([]int{1, 2}, []int{3, 4}...)
答案3
得分: 62
我想强调一下 @icza 的答案并简化一下,因为这是一个关键的概念。我假设读者对slices很熟悉。
c := append(a, b...)
这是一个有效的答案。
但是,如果你需要在代码的不同上下文中稍后使用slices 'a'和'c',这不是一种安全的连接slices的方式。
为了解释这个问题,让我们不从slices的角度来看待表达式,而是从底层数组的角度来看:
> "取(底层)数组'a'并将数组'b'的元素追加到其中。如果数组'a'有足够的容量来包含来自'b'的所有元素,那么数组'c'的底层数组将不是一个新数组,而实际上是数组'a'。基本上,slice 'a'将显示底层数组'a'的len(a)个元素,而slice 'c'将显示数组'a'的len(c)个元素。"
append()函数不一定会创建一个新的数组!这可能导致意外的结果。请参见Go Playground示例。
如果您想确保为slice分配新的数组,请始终使用make()函数。例如,以下是一些丑陋但足够高效的任务选项。
la := len(a)
c := make([]int, la, la + len(b))
_ = copy(c, a)
c = append(c, b...)
la := len(a)
c := make([]int, la + len(b))
_ = copy(c, a)
_ = copy(c[la:], b)
英文:
I would like to emphasize @icza answer and simplify it a bit since it is a crucial concept. I assume that reader is familiar with slices.
c := append(a, b...)
This is a valid answer to the question.
BUT if you need to use slices 'a' and 'c' later in code in different context, this is not the safe way to concatenate slices.
To explain, lets read the expression not in terms of slices, but in terms of underlying arrays:
> "Take (underlying) array of 'a' and append elements from array 'b' to
> it. If array 'a' has enough capacity to include all elements from 'b'
> - underlying array of 'c' will not be a new array, it will actually be array 'a'. Basically, slice 'a' will show len(a) elements of
> underlying array 'a', and slice 'c' will show len(c) of array 'a'."
append() does not necessarily create a new array! This can lead to unexpected results. See Go Playground example.
Always use make() function if you want to make sure that new array is allocated for the slice. For example here are few ugly but efficient enough options for the task.
la := len(a)
c := make([]int, la, la + len(b))
_ = copy(c, a)
c = append(c, b...)
la := len(a)
c := make([]int, la + len(b))
_ = copy(c, a)
_ = copy(c[la:], b)
答案4
得分: 57
没有对其他答案有任何意见,但我发现文档中的简要解释比其中的示例更容易理解:
> ## func append
>
> func append(slice []Type, elems ...Type) []Type
append内置函数将元素追加到切片的末尾。如果切片有足够的容量,目标切片将被重新切片以容纳新元素。如果没有足够的容量,将分配一个新的底层数组。append返回更新后的切片。因此,有必要将append的结果存储在变量中,通常是持有切片本身的变量:
>
> slice = append(slice, elem1, elem2)
> slice = append(slice, anotherSlice...)
>
> 作为特例,将字符串追加到字节切片是合法的,如下所示:
>
> slice = append([]byte("hello "), "world"...)
英文:
Nothing against the other answers, but I found the brief explanation in the docs more easily understandable than the examples in them:
> ## func append
>
> func append(slice []Type, elems ...Type) []Type
The append built-in
> function appends elements to the end of a slice. If it has sufficient
> capacity, the destination is resliced to accommodate the new elements.
> If it does not, a new underlying array will be allocated. Append
> returns the updated slice. It is therefore necessary to store the
> result of append, often in the variable holding the slice itself:
>
> slice = append(slice, elem1, elem2)
> slice = append(slice, anotherSlice...)
>
> As a special case, it is legal to append a string to a byte slice,
> like this:
>
> slice = append([]byte("hello "), "world"...)
答案5
得分: 37
我认为指出并了解以下事实很重要:如果目标切片(即您要附加到的切片)具有足够的容量,附加操作将“原地”发生,通过重新切片目标(通过增加其长度以容纳可附加元素)。
这意味着,如果目标是通过对更大的数组或切片进行切片创建的,而该数组或切片的长度超出了结果切片的长度,那么它们可能会被覆盖。
为了演示这一点,看看这个例子:
a := [10]int{1, 2}
fmt.Printf("a: %v\n", a)
x, y := a[:2], []int{3, 4}
fmt.Printf("x: %v, y: %v\n", x, y)
fmt.Printf("cap(x): %v\n", cap(x))
x = append(x, y...)
fmt.Printf("x: %v\n", x)
fmt.Printf("a: %v\n", a)
输出结果(在Go Playground上尝试):
a: [1 2 0 0 0 0 0 0 0 0]
x: [1 2], y: [3 4]
cap(x): 10
x: [1 2 3 4]
a: [1 2 3 4 0 0 0 0 0 0]
我们创建了一个长度为10的“支持”数组a
。然后我们通过对该a
数组进行切片创建了目标切片x
,使用复合字面量[]int{3, 4}
创建了切片y
。现在,当我们将y
附加到x
时,结果是预期的[1 2 3 4]
,但可能令人惊讶的是,支持数组a
也发生了变化,因为x
的容量是10
,足以将y
附加到其中,所以x
被重新切片,也将使用相同的支持数组a
,并且append()
将元素从y
复制到其中。
如果您想避免这种情况,可以使用完整切片表达式,其形式为
a[low : high : max]
它构造了一个切片,并通过将其设置为max - low
来控制结果切片的容量。
看看修改后的示例(唯一的区别是我们这样创建了x
:x = a[:2:2]
):
a := [10]int{1, 2}
fmt.Printf("a: %v\n", a)
x, y := a[:2:2], []int{3, 4}
fmt.Printf("x: %v, y: %v\n", x, y)
fmt.Printf("cap(x): %v\n", cap(x))
x = append(x, y...)
fmt.Printf("x: %v\n", x)
fmt.Printf("a: %v\n", a)
输出结果(在Go Playground上尝试):
a: [1 2 0 0 0 0 0 0 0 0]
x: [1 2], y: [3 4]
cap(x): 2
x: [1 2 3 4]
a: [1 2 0 0 0 0 0 0 0 0]
如您所见,我们得到了相同的x
结果,但支持数组a
没有改变,因为x
的容量仅为2
(得益于完整切片表达式a[:2:2]
)。因此,为了进行附加操作,将分配一个新的支持数组,可以存储x
和y
的元素,该数组与a
不同。
英文:
I think it's important to point out and to know that if the destination slice (the slice you append to) has sufficient capacity, the append will happen "in-place", by reslicing the destination (reslicing to increase its length in order to be able to accommodate the appendable elements).
This means that if the destination was created by slicing a bigger array or slice which has additional elements beyond the length of the resulting slice, they may get overwritten.
To demonstrate, see this example:
a := [10]int{1, 2}
fmt.Printf("a: %v\n", a)
x, y := a[:2], []int{3, 4}
fmt.Printf("x: %v, y: %v\n", x, y)
fmt.Printf("cap(x): %v\n", cap(x))
x = append(x, y...)
fmt.Printf("x: %v\n", x)
fmt.Printf("a: %v\n", a)
Output (try it on the Go Playground):
a: [1 2 0 0 0 0 0 0 0 0]
x: [1 2], y: [3 4]
cap(x): 10
x: [1 2 3 4]
a: [1 2 3 4 0 0 0 0 0 0]
We created a "backing" array a
with length 10
. Then we create the x
destination slice by slicing this a
array, y
slice is created using the composite literal []int{3, 4}
. Now when we append y
to x
, the result is the expected [1 2 3 4]
, but what may be surprising is that the backing array a
also changed, because capacity of x
is 10
which is sufficient to append y
to it, so x
is resliced which will also use the same a
backing array, and append()
will copy elements of y
into there.
If you want to avoid this, you may use a full slice expression which has the form
a[low : high : max]
which constructs a slice and also controls the resulting slice's capacity by setting it to max - low
.
See the modified example (the only difference is that we create x
like this: x = a[:2:2]
:
a := [10]int{1, 2}
fmt.Printf("a: %v\n", a)
x, y := a[:2:2], []int{3, 4}
fmt.Printf("x: %v, y: %v\n", x, y)
fmt.Printf("cap(x): %v\n", cap(x))
x = append(x, y...)
fmt.Printf("x: %v\n", x)
fmt.Printf("a: %v\n", a)
Output (try it on the Go Playground)
a: [1 2 0 0 0 0 0 0 0 0]
x: [1 2], y: [3 4]
cap(x): 2
x: [1 2 3 4]
a: [1 2 0 0 0 0 0 0 0 0]
As you can see, we get the same x
result but the backing array a
did not change, because capacity of x
was "only" 2
(thanks to the full slice expression a[:2:2]
). So to do the append, a new backing array is allocated that can store the elements of both x
and y
, which is distinct from a
.
答案6
得分: 9
append()函数和扩展运算符
可以使用标准的golang库中的**append
**方法来连接两个切片。这类似于variadic
函数操作。因此我们需要使用...
package main
import (
"fmt"
)
func main() {
x := []int{1, 2, 3}
y := []int{4, 5, 6}
z := append([]int{}, append(x, y...)...)
fmt.Println(z)
}
上述代码的输出是:[1 2 3 4 5 6]
英文:
append( ) function and spread operator
Two slices can be concatenated using append
method in the standard golang library. Which is similar to the variadic
function operation. So we need to use ...
package main
import (
"fmt"
)
func main() {
x := []int{1, 2, 3}
y := []int{4, 5, 6}
z := append([]int{}, append(x, y...)...)
fmt.Println(z)
}
> output of the above code is: [1 2 3 4 5 6]
答案7
得分: 6
要连接两个切片,
func main() {
s1 := []int{1, 2, 3}
s2 := []int{99, 100}
s1 = append(s1, s2...)
fmt.Println(s1) // [1 2 3 99 100]
}
要将单个值追加到切片中
func main() {
s1 := []int{1, 2, 3}
s1 = append(s1, 4)
fmt.Println(s1) // [1 2 3 4]
}
要将多个值追加到切片中
func main() {
s1 := []int{1, 2, 3}
s1 = append(s1, 4, 5)
fmt.Println(s1) // [1 2 3 4 5]
}
英文:
To concatenate two slices,
func main() {
s1 := []int{1, 2, 3}
s2 := []int{99, 100}
s1 = append(s1, s2...)
fmt.Println(s1) // [1 2 3 99 100]
}
To append a single value to a slice
func main() {
s1 := []int{1,2,3}
s1 := append(s1, 4)
fmt.Println(s1) // [1 2 3 4]
}
To append multiple values to a slice
func main() {
s1 := []int{1,2,3}
s1 = append(s1, 4, 5)
fmt.Println(s1) // [1 2 3 4]
}
答案8
得分: 5
似乎泛型是一个完美的使用场景(如果使用1.18或更高版本)。
func concat[T any](first []T, second []T) []T {
n := len(first);
return append(first[:n:n], second...);
}
英文:
Seems like a perfect use for generics (if using 1.18 or later).
func concat[T any](first []T, second []T) []T {
n := len(first);
return append(first[:n:n], second...);
}
答案9
得分: 3
append([]int{1,2}, []int{3,4}...)
将会起作用。将参数传递给 ...
参数。
如果 f
是一个带有类型为 ...T
的最后一个参数 p
的可变参数函数,那么在 f
内部,p
的类型等同于 []T
类型。
如果在调用 f
时没有为 p
提供实际参数,那么传递给 p
的值将为 nil
。
否则,传递的值将是一个新的 []T
类型的切片,其底层数组的连续元素是实际的参数,而这些参数都必须可赋值给 T
。因此,切片的长度和容量将取决于绑定到 p
的参数数量,并且可能在每个调用点上有所不同。
给定以下函数和调用:
func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")
英文:
append([]int{1,2}, []int{3,4}...)
will work. Passing arguments to ...
parameters.
If f
is variadic with a final parameter p
of type ...T
, then within f
the type of p
is equivalent to type []T
.
If f
is invoked with no actual arguments for p
, the value passed to p
is nil
.
Otherwise, the value passed is a new slice of type []T
with a new underlying array whose successive elements are the actual arguments, which all must be assignable to T
. The length and capacity of the slice is therefore the number of arguments bound to p
and may differ for each call site.
Given the function and calls
func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论