Xóa phần tử trong một lát


139
func main() {
    a := []string{"Hello1", "Hello2", "Hello3"}
    fmt.Println(a)
    // [Hello1 Hello2 Hello3]
    a = append(a[:0], a[1:]...)
    fmt.Println(a)
    // [Hello2 Hello3]
}

Làm thế nào để xóa thủ thuật với chức năng chắp thêm hoạt động?

Có vẻ như nó lấy tất cả mọi thứ trước phần tử đầu tiên (mảng trống)

Sau đó nối thêm mọi thứ sau phần tử đầu tiên (vị trí 0)

... (chấm chấm chấm) làm gì?



3
Tại sao không xem qua thông số ngôn ngữ? ...được giải thích chi tiết ở đó?
Volker

9
Một mặt, hoàn toàn đúng ( golang.org/ref/spec , tất cả mọi người); mặt khác, có đủ lạ lẫm về những thành ngữ này để di chuyển Pythonistas, v.v. mà tôi không ngại có một lời giải thích ngoài kia cho người khác tìm thấy.
twotwotwo

Câu trả lời:


276

Đâu alà lát cắt và ilà chỉ mục của phần tử bạn muốn xóa:

a = append(a[:i], a[i+1:]...)

... là cú pháp cho các đối số biến đổi trong Go.

Về cơ bản, khi xác định hàm, nó đặt tất cả các đối số mà bạn chuyển vào một lát của kiểu đó. Bằng cách đó, bạn có thể truyền nhiều đối số như bạn muốn (ví dụ: fmt.Printlncó thể lấy bao nhiêu đối số bạn muốn).

Bây giờ, khi gọi một hàm, ...thực hiện ngược lại: nó giải nén một lát và chuyển chúng dưới dạng các đối số riêng biệt cho một hàm matrixdic.

Vì vậy, dòng này làm gì:

a = append(a[:0], a[1:]...)

về cơ bản là:

a = append(a[:0], a[1], a[2])

Bây giờ, bạn có thể tự hỏi, tại sao không làm

a = append(a[1:]...)

Vâng, định nghĩa hàm append

func append(slice []Type, elems ...Type) []Type

Vì vậy, đối số đầu tiên phải là một lát cắt đúng loại, đối số thứ hai là biến thể, vì vậy chúng ta chuyển vào một lát trống và sau đó giải nén phần còn lại của lát cắt để điền vào các đối số.


35
Bạn không thoát khỏi phạm vi ngoại lệ nếu tôi là thành phần cuối cùng từ lát cắt? a = append(a[:i], a[i+1:]...)
themihai

5
@DaveC Tôi gặp lỗi đó khi làm việc với các lát cắt trong dự án của mình: /
Tyguy7

3
@ Tyguy7 từ thông số kỹ thuật: "Đối với mảng hoặc chuỗi, các chỉ số nằm trong phạm vi nếu 0 <= low <= high <= len (a), nếu không chúng nằm ngoài phạm vi." Có thể trong trường hợp của bạn cao <thấp; trong trường hợp đó bạn sẽ nhận được lỗi. ( golang.org/ref/spec#Slice_expressions )
mlg

2
Làm thế nào là hiệu suất của điều này? Tôi thực sự hy vọng nó không tạo ra một lát cắt hoàn toàn mới dưới mui xe ..
joonas.fi

7
@ Tyguy7 Tôi nghĩ bạn đã cố xóa các phần tử của lát trong vòng lặp. Vì vậy, bạn phải cẩn thận với các chỉ số.
Nikolay Bystritskiy

42

Có hai lựa chọn:

A: Bạn quan tâm đến việc giữ lại thứ tự mảng:

a = append(a[:i], a[i+1:]...)
// or
a = a[:i+copy(a[i:], a[i+1:])]

B: Bạn không quan tâm đến việc giữ lại trật tự (điều này có thể nhanh hơn):

a[i] = a[len(a)-1] // Replace it with the last one. CAREFUL only works if you have enough elements.
a = a[:len(a)-1]   // Chop off the last one.

Xem liên kết để xem hàm ý rò rỉ bộ nhớ nếu mảng của bạn là con trỏ.

https://github.com/golang/go/wiki/SliceTricks


Điều này thật thú vị, nhưng không thực sự trả lời câu hỏi
Bryan

Điều này thật tuyệt, nhưng sẽ tốt hơn nếu nó có thể xóa phần tử duy nhất trong mảng
Naguib Ihab

2
Chỉ cần ngẩng cao đầu, cố gắng sử dụng cái đầu tiên từ b (thay thế bằng phần tử cuối cùng) rõ ràng không hoạt động nếu bạn đang cố xóa phần tử cuối cùng trong lát cắt lol
Sirens

13

Thay vì nghĩ các chỉ số trong [a:]-, [:b]- và [a:b]chú thích là chỉ số phần tử, hãy nghĩ về chúng như các chỉ số của khoảng trống xung quanh và giữa các phần tử, bắt đầu bằng khoảng cách được lập chỉ mục 0trước khi phần tử được lập chỉ mục là 0.

nhập mô tả hình ảnh ở đây

Chỉ nhìn vào những con số màu xanh, sẽ dễ dàng hơn nhiều để thấy những gì đang diễn ra: [0:3]bao quanh mọi thứ, [3:3]trống rỗng và [1:2]sẽ mang lại {"B"}. Sau đó, [a:]chỉ là phiên bản ngắn của [a:len(arrayOrSlice)], [:b]phiên bản ngắn [0:b][:]phiên bản ngắn của [0:len(arrayOrSlice)]. Cái sau thường được sử dụng để biến một mảng thành một lát khi cần thiết.


1
Điều này giúp giải thích lý do tại sao câu trả lời là "không" đối với nhận xét của họ về câu trả lời của dave , đề cập đến [i + 1:] ngay cả khi đề cập đến yếu tố thứ i: play.golang.org/p/E0lQ3jPcjX5
Nick P

5

... là cú pháp cho các đối số kiểu chữ.

Tôi nghĩ rằng nó được trình biên dịch triển khai bằng cách sử dụng lát cắt ( []Type), giống như chức năng nối thêm:

func append(slice []Type, elems ...Type) []Type

khi bạn sử dụng "elems" trong "append", thực tế nó là một lát (loại []). Vậy " a = append(a[:0], a[1:]...)" có nghĩa là " a = append(a[0:0], a[1:])"

a[0:0] là một lát không có gì

a[1:] là "Hello2 Hello3"

Đây là cách nó hoạt động


2
a[0:0]không phải là nilmột lát có độ dài 0. a[0:0]sẽ chỉnilnếu ađược nil.
icza

5

Tôi nhận được một chỉ mục nằm ngoài phạm vi lỗi với giải pháp trả lời được chấp nhận. Lý do: Khi phạm vi bắt đầu, nó không lặp lại giá trị từng cái một, nó được lặp theo chỉ số. Nếu bạn sửa đổi một lát trong khi nó nằm trong phạm vi, nó sẽ gây ra một số vấn đề.

Câu trả lời cũ:

chars := []string{"a", "a", "b"}

for i, v := range chars {
    fmt.Printf("%+v, %d, %s\n", chars, i, v)
    if v == "a" {
        chars = append(chars[:i], chars[i+1:]...)
    }
}
fmt.Printf("%+v", chars)

Hy vọng :

[a a b], 0, a
[a b], 0, a
[b], 0, b
Result: [b]

Thực tế:

// Autual
[a a b], 0, a
[a b], 1, b
[a b], 2, b
Result: [a b]

Cách chính xác (Giải pháp):

chars := []string{"a", "a", "b"}

for i := 0; i < len(chars); i++ {
    if chars[i] == "a" {
        chars = append(chars[:i], chars[i+1:]...)
        i-- // form the remove item index to start iterate next item
    }
}

fmt.Printf("%+v", chars)

Nguồn: https://dinolai.com/notes/golang/golang-delete-slice-item-in-range-probols.html


3

Trong wiki của golang, nó hiển thị một số thủ thuật cho lát, bao gồm xóa một phần tử khỏi lát.

Liên kết: nhập mô tả liên kết tại đây

Ví dụ a là lát mà bạn muốn xóa phần tử số i.

a = append(a[:i], a[i+1:]...)

HOẶC LÀ

a = a[:i+copy(a[i:], a[i+1:])]
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.