Usuń element w plasterku


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

Jak działa ta sztuczka usuwania z funkcją dołączania?

Wydawałoby się, że przechwytuje wszystko przed pierwszym elementem (pusta tablica)

Następnie dołączanie wszystkiego po pierwszym elemencie (pozycja zero)

Co robi ... (kropka kropka kropka)?



3
Dlaczego nie spojrzeć na specyfikację językową? ...jest tam szczegółowo wyjaśnione?
Volker

9
Z jednej strony, absolutna prawda ( golang.org/ref/spec , wszyscy); z drugiej strony jest wystarczająco dużo nieznajomości tych idiomów do migracji Pythonistas itp., że nie mam nic przeciwko temu, aby inni mogli je znaleźć.
twotwotwo

Odpowiedzi:


276

Gdzie ajest wycinek, a ijest indeksem elementu, który chcesz usunąć:

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

... to składnia argumentów wariadycznych w Go.

Zasadniczo podczas definiowania funkcji umieszcza wszystkie przekazywane argumenty w jednym wycinku tego typu. W ten sposób możesz przekazać dowolną liczbę argumentów (na przykład fmt.Printlnmożesz przyjąć dowolną liczbę argumentów).

Teraz, wywołując funkcję, ...robi odwrotnie: rozpakowuje plasterek i przekazuje je jako oddzielne argumenty do funkcji wariadycznej.

Więc co robi ta linia:

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

to zasadniczo:

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

Teraz możesz się zastanawiać, dlaczego po prostu tego nie zrobić

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

Cóż, definicja funkcji appendto

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

Więc pierwszy argument musi być kawałkiem odpowiedniego typu, drugi argumentem jest wariadą, więc przekazujemy pusty wycinek, a następnie rozpakowujemy resztę wycinka, aby wypełnić argumenty.


35
Czy nie wychodzisz poza zasięg wyjątku, jeśli i jest ostatnim elementem z wycinka? a = append(a[:i], a[i+1:]...)
themihai

5
@DaveC Otrzymuję ten błąd podczas pracy z moimi plasterkami w moim projekcie: /
Tyguy7

3
@ Tyguy7 ze specyfikacji: „W przypadku tablic lub ciągów indeksy mieszczą się w zakresie, jeśli 0 <= niski <= wysoki <= len (a), w przeciwnym razie są poza zakresem.” Może w twoim przypadku wysoki <niski; w takim przypadku otrzymasz błąd. ( golang.org/ref/spec#Slice_expressions )
mlg,

2
Jak to działa? Naprawdę mam nadzieję, że nie stworzy to zupełnie nowego kawałka pod maską ...
joonas.fi

7
@ Tyguy7 Myślę, że próbowałeś usunąć elementy plasterka w pętli. Musisz więc uważać na indeksy.
Nikolay Bystritskiy

42

Istnieją dwie możliwości:

O: Zależy Ci na zachowaniu kolejności tablic:

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

B: Nie zależy Ci na zachowaniu kolejności (prawdopodobnie jest to szybsze):

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.

Zobacz link, aby zobaczyć konsekwencje wycieków pamięci, jeśli twoja tablica zawiera wskaźniki.

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


To interesujące, ale nie do końca odpowiadające na pytanie
Bryan

To fajne, ale byłoby lepiej, gdyby można było usunąć jedyny element w tablicy
Naguib Ihab

2
Tylko jedno ostrzeżenie, próba użycia pierwszego z b (zastąp ostatnim elementem) nie działa oczywiście, jeśli próbujesz usunąć ostatni element w kawałku lol
Sirens

13

Zamiast myśleć o indeksach w adnotacjach [a:]-, [:b]- i - [a:b]jako o indeksach elementów, myśl o nich jako o indeksach luk wokół i między elementami, zaczynając od przerwy indeksowanej 0przed elementem indeksowanym jako 0.

wprowadź opis obrazu tutaj

Patrząc tylko na niebieskie liczby, znacznie łatwiej jest zobaczyć, co się dzieje: [0:3]obejmuje wszystko, [3:3]jest puste i [1:2]ustąpi {"B"}. Następnie [a:]jest tylko skrócona wersja [a:len(arrayOrSlice)], [:b]skrócona wersja [0:b]i [:]skrócona wersja [0:len(arrayOrSlice)]. Ten ostatni jest powszechnie używany do przekształcania tablicy w plasterek, gdy jest to potrzebne.


1
To pomaga wyjaśnić, dlaczego odpowiedź brzmi „nie” themihai męska komentarzu na Dave'a odpowiedź , odwołując się do [i + 1:] nawet gdy odnosi się do i-tego elementu: play.golang.org/p/E0lQ3jPcjX5
Nick P

5

... jest składnią argumentów wielorakich.

Myślę, że jest to implementowane przez osobę odpowiedzialną za pomocą plastra ( []Type)podobnie jak funkcja append:

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

kiedy używasz "elems" w "append", w rzeczywistości jest to kawałek (typ []). Więc „ a = append(a[:0], a[1:]...)” oznacza „ a = append(a[0:0], a[1:])

a[0:0] to kawałek, który nie ma nic

a[1:] to „Hello2 Hello3”

Tak to działa


2
a[0:0]to nie niltylko kawałek o zerowej długości. a[0:0]będzie tylko być nil, jeśli ajest nil.
icza

5

Otrzymuję indeks poza zakresem z zaakceptowanym rozwiązaniem odpowiedzi. Powód: kiedy zaczyna się zakres, nie jest to iteracja wartości jeden po drugim, lecz iteracja według indeksu. Jeśli zmodyfikowałeś kawałek, gdy był w zasięgu, spowoduje to pewien problem.

Stara odpowiedź:

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)

Spodziewany :

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

Rzeczywisty:

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

Właściwy sposób (rozwiązanie):

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)

Źródło: https://dinolai.com/notes/golang/golang-delete-slice-item-in-range-problem.html


3

Na wiki golanga pokazuje kilka sztuczek do plasterka, w tym usuwanie elementu z plasterka.

Link: wprowadź opis linku tutaj

Na przykład a jest wycinkiem, z którego chcesz usunąć element numer i.

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

LUB

a = a[:i+copy(a[i:], a[i+1:])]
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.