Nối hai lát trong Go


477

Tôi đang cố gắng kết hợp lát cắt [1, 2]và lát cắt [3, 4]. Làm thế nào tôi có thể làm điều này trong Go?

Tôi đã thử:

append([]int{1,2}, []int{3,4})

nhưng có:

cannot use []int literal (type []int) as type int in append

Tuy nhiên, tài liệu dường như chỉ ra điều này là có thể, tôi còn thiếu gì?

slice = append(slice, anotherSlice...)

Câu trả lời:


878

Thêm dấu chấm sau lát thứ hai:

//---------------------------vvv
append([]int{1,2}, []int{3,4}...)

Điều này cũng giống như bất kỳ chức năng matrixdic khác.

func foo(is ...int) {
    for i := 0; i < len(is); i++ {
        fmt.Println(is[i])
    }
}

func main() {
    foo([]int{9,8,7,6,5}...)
}

37
append()một hàm matrixdic và ...cho phép bạn truyền nhiều đối số cho một hàm matrixdic từ một lát cắt.

11
Đây có phải là tất cả biểu diễn khi các lát khá lớn? Hoặc trình biên dịch không thực sự vượt qua tất cả các yếu tố như tham số?
cóc

15
@Toad: Nó không thực sự lan truyền chúng ra. Trong foo()ví dụ trên, istham số giữ một bản sao của lát cắt gốc, nghĩa là nó có một bản sao của tham chiếu trọng lượng nhẹ đến cùng một mảng, len và nắp bên dưới. Nếu foochức năng thay đổi thành viên, thay đổi sẽ được nhìn thấy trên bản gốc. Đây là một bản demo . Vì vậy, chi phí thực tế duy nhất sẽ là nó tạo ra một lát cắt mới nếu bạn chưa có một lát cắt nào, như: foo(1, 2, 3, 4, 5)nó sẽ tạo ra một lát cắt mới issẽ giữ.

2
Ah. Nếu tôi hiểu chính xác, hàm matrixdic thực sự được triển khai như một mảng các tham số (thay vì mọi tham số trên ngăn xếp)? Và vì bạn vượt qua trong lát cắt, nó thực sự ánh xạ từng cái một?
cóc

@Toad: Có, khi bạn sử dụng ...trên một lát cắt hiện có, nó chỉ đơn giản là vượt qua lát cắt đó. Khi bạn vượt qua các đối số riêng lẻ, nó tập hợp chúng thành một lát cắt mới và vượt qua nó. Tôi không có kiến ​​thức trực tiếp về các cơ chế chính xác, nhưng tôi đoán rằng: foo(1, 2, 3, 4, 5)và điều này: func foo(is ...int) {chỉ là đường khử cho điều này: foo([]int{1, 2, 3, 4, 5})và điều này : func foo(is []int) {.

77

Nối và sao chép lát

Hàm matrixdic appendnối thêm 0 hoặc nhiều giá trị xvào s loại S, phải là loại lát và trả về lát cắt kết quả, cũng thuộc loại S. Các giá trị xđược truyền cho một tham số kiểu ...Ttrong đó Tloại phần tử Svà áp dụng quy tắc chuyển tham số tương ứng. Trong trường hợp đặc biệt, chắp thêm cũng chấp nhận một đối số đầu tiên có thể gán được để nhập []bytevới một đối số thứ hai của stringloại theo sau .... Biểu mẫu này nối thêm các byte của chuỗi.

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}

Truyền đối số cho ... tham số

Nếu flà matrixdic với loại tham số cuối cùng ...T, thì trong hàm, đối số tương đương với tham số của loại []T. Tại mỗi lệnh gọi của f, đối số được truyền cho tham số cuối cùng là một lát kiểu mới []Tcó các phần tử liên tiếp là các đối số thực tế, tất cả phải được gán cho loại T. Do đó, độ dài của lát cắt là số lượng đối số được liên kết với tham số cuối cùng và có thể khác nhau cho mỗi trang web cuộc gọi.

Câu trả lời cho câu hỏi của bạn là ví dụ s3 := append(s2, s0...)trong Đặc tả ngôn ngữ lập trình Go . Ví dụ,

s := append([]int{1, 2}, []int{3, 4}...)

6
Lưu ý: việc sử dụng chung của append (lát1, lát2 ...) có vẻ khá nguy hiểm đối với tôi. Nếu lát1 là một lát của một mảng lớn hơn, các giá trị của mảng đó sẽ bị ghi đè bởi lát2. (Nó khiến tôi co rúm người lại vì điều này dường như không phải là mối quan tâm chung?)
Hugo

7
@Hugo Nếu bạn "bàn giao" một lát của mảng, thì hãy biết rằng "chủ sở hữu" của lát sẽ có thể nhìn thấy / ghi đè lên các phần của mảng vượt quá độ dài hiện tại của lát. Nếu bạn không muốn điều này, bạn có thể sử dụng biểu thức lát đầy đủ (ở dạng a[low : high : max]) cũng chỉ định dung lượng tối đa . Ví dụ, lát cắt a[0:2:4]sẽ có dung lượng 4và nó không thể được đặt lại để bao gồm các phần tử nằm ngoài điều đó, ngay cả khi mảng sao lưu có hàng nghìn phần tử sau đó.
icza 17/2/2016

30

Không có gì chống lại các câu trả lời khác, nhưng tôi thấy lời giải thích ngắn gọn trong các tài liệu dễ hiểu hơn các ví dụ trong đó:

nối thêm

func append(slice []Type, elems ...Type) []TypeHàm tích hợp bổ sung sẽ nối các phần tử vào cuối một lát. Nếu nó có đủ năng lực, đích sẽ được nối lại để chứa các phần tử mới. Nếu không, một mảng cơ bản mới sẽ được phân bổ. Nối trả về lát đã cập nhật. Do đó, cần lưu trữ kết quả của phần bổ sung, thường là trong biến giữ chính lát cắt:

slice = append(slice, elem1, elem2)
slice = append(slice, anotherSlice...)

Trong trường hợp đặc biệt, việc nối một chuỗi vào một lát byte là hợp pháp, như sau:

slice = append([]byte("hello "), "world"...)

1
Cảm ơn bạn! Có giá trị đối với tôi!
Korjavin Ivan

23

Tôi nghĩ điều quan trọng là phải chỉ ra và phải biết rằng nếu lát đích (lát cắt mà bạn nối thêm) có đủ năng lực, thì phần bổ sung sẽ xảy ra "tại chỗ", bằng cách nối lại đích (thay đổi để tăng độ dài của nó có thể chứa các yếu tố nối thêm).

Điều này có nghĩa là nếu đích được tạo bằng cách cắt một mảng hoặc lát lớn hơn có các phần tử bổ sung vượt quá độ dài của lát kết quả, chúng có thể bị ghi đè.

Để chứng minh, xem ví dụ này:

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)

Đầu ra (thử trên Sân chơi Go ):

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]

Chúng tôi tạo ra một mảng "sao lưu" avới chiều dài 10. Sau đó, chúng ta tạo xlát cắt đích bằng cách cắt amảng này , ylát được tạo bằng chữ hỗn hợp []int{3, 4}. Bây giờ khi chúng tôi thêm yvào x, kết quả là dự kiến [1 2 3 4], nhưng những gì có thể được ngạc nhiên là các mảng ủng hộ acũng đã thay đổi, bởi vì công suất x10đó là đủ để nối thêm yvào nó, vì vậy xđược resliced mà cũng sẽ sử dụng cùng một amảng ủng hộ, vàappend() sẽ sao chép các yếu tố yvào đó.

Nếu bạn muốn tránh điều này, bạn có thể sử dụng biểu thức lát đầy đủ có dạng

a[low : high : max]

mà xây dựng một lát và cũng kiểm soát dung lượng của lát cắt kết quả bằng cách đặt nó thành max - low.

Xem ví dụ đã sửa đổi (sự khác biệt duy nhất là chúng tôi tạo xnhư thế này 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)

Đầu ra (thử trên Sân chơi Go )

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]

Như bạn có thể thấy, chúng tôi nhận được xkết quả tương tự nhưng mảng sao lưu akhông thay đổi, vì dung lượng xlà "chỉ" 2(nhờ biểu thức lát đầy đủ a[:2:2]). Vì vậy, để thực hiện nối thêm, một mảng sao lưu mới được phân bổ có thể lưu trữ các phần tử của cả hai xy, khác vớia .


2
Nó rất hữu ích cho vấn đề tôi gặp phải. Cảm ơn.
Aidy

9

Tôi muốn nhấn mạnh câu trả lời @icza và đơn giản hóa nó một chút vì đây là một khái niệm quan trọng. Tôi cho rằng người đọc quen thuộc với các lát .

c := append(a, b...)

Đây là một câu trả lời hợp lệ cho câu hỏi. NHƯNG nếu bạn cần sử dụng các lát 'a' và 'c' sau đó trong mã trong các ngữ cảnh khác nhau, đây không phải là cách an toàn để ghép các lát.

Để giải thích, hãy đọc biểu thức không phải bằng các lát, mà về các mảng cơ bản:

"Lấy mảng (bên dưới) của các phần tử 'a' và nối các phần tử từ mảng 'b' vào nó. Nếu mảng 'a' có đủ khả năng bao gồm tất cả các phần tử từ 'b' - mảng bên dưới của 'c' sẽ không phải là một mảng mới , nó thực sự sẽ là mảng 'a'. Về cơ bản, lát 'a' sẽ hiển thị các phần tử len (a) của mảng cơ bản 'a' và lát 'c' sẽ hiển thị len (c) của mảng 'a'. "

append () không nhất thiết phải tạo một mảng mới! Điều này có thể dẫn đến kết quả bất ngờ. Xem ví dụ về Sân chơi .

Luôn sử dụng hàm make () nếu bạn muốn đảm bảo rằng mảng mới được phân bổ cho lát cắt. Ví dụ ở đây là một vài lựa chọn xấu xí nhưng đủ hiệu quả cho nhiệm vụ.

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)

Cảm ơn đã chỉ ra những tác dụng phụ. Tuyệt vời tương phản với szenario sửa đổi này. play.golang.org/p/9FKo5idLBj4 Mặc dù khi cung cấp dung lượng vượt mức, người ta nên suy nghĩ cẩn thận về những tác dụng phụ khó hiểu này chống lại trực giác chính đáng.
olippuner

5

hàm append () và toán tử trải

Hai lát có thể được nối bằng appendphương pháp trong thư viện golang tiêu chuẩn. Mà tương tự như variadichoạt động chức năng. Vì vậy, chúng ta cần sử dụng...

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)
}

đầu ra của đoạn mã trên là: [1 2 3 4 5 6]


2

append([]int{1,2}, []int{3,4}...)sẽ làm việc. Truyền đối số cho... tham số.

Nếu flà matrixdic với một tham số cuối cùng pcủa loại ...T, thì trong floại ptương đương với loại []T.

Nếu fđược gọi mà không có đối số thực tế cho p, giá trị được chuyển đến pnil.

Mặt khác, giá trị được truyền là một lát kiểu mới []Tvới một mảng bên dưới mới có các phần tử liên tiếp là các đối số thực tế, tất cả phải được gán cho T. Do đó, độ dài và dung lượng của lát cắt là số lượng đối số được liên kết pvà có thể khác nhau cho mỗi trang web cuộc gọi.

Đưa ra chức năng và các cuộc gọi

func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.