2013-09-02 15 views
9

Go ponownie zaskoczył mnie. Mam nadzieję, że ktoś może pomóc. Stworzyłem plaster (mySlice), który zawiera wskaźniki do struktur (myStruct).Jak usunąć element z plasterka, wywołując metodę na plasterku

Problem stanowi metoda "Usuń". Kiedy jesteśmy w "Remove" wszystko jest w porządku, ale gdy wrócimy, rozmiar plastra się nie zmienił, więc widzimy ostatni element wymieniony dwa razy.

Pierwotnie próbowałem pisać "Usuń" przy użyciu tego samego wzorca stosowanego w metodzie "Dodaj", ale nie skompilowałoby się i zostało skomentowane.

Mogę go uruchomić, zwracając nowo utworzony plaster do funkcji wywołującej, ale nie chcę tego robić, ponieważ mySlice (ms) jest singletonem.

A gdybym nie zadał już dosyć ...

Kod na „Dodaj” metoda działa, choć nie jestem pewien, w jaki sposób. Z tego co mogę zebrać "Add" otrzymuje wskaźnik do nagłówka plasterka (3 element "struct"). Z tego, co przeczytałem, długość i pojemność plasterka nie są przekazywane do metod (gdy przechodzą przez wartość), więc być może podanie wskaźnika do plasterka pozwala tej metodzie zobaczyć i wykorzystać długość i pojemność, umożliwiając nam dołączyć". Jeśli to prawda, to dlaczego ten sam wzór nie działa w "Usuń"?

Bardzo dziękuję za wszystkie spostrzeżenia i pomoc!

package main 

import (
    "fmt" 
) 

type myStruct struct { 
    a int 
} 
type mySlice []*myStruct 

func (slc *mySlice) Add(str *myStruct) { 
    *slc = append(*slc, str) 
} 

//does not compile with reason: cannot slice slc (type *mySlice) 
//func (slc *mySlice) Remove1(item int) { 
// *slc = append(*slc[:item], *slc[item+1:]...) 
//} 

func (slc mySlice) Remove(item int) { 
    slc = append(slc[:item], slc[item+1:]...) 
    fmt.Printf("Inside Remove = %s\n", slc) 
} 

func main() { 
    ms := make(mySlice, 0) 
    ms.Add(&myStruct{0}) 
    ms.Add(&myStruct{1}) 
    ms.Add(&myStruct{2}) 
    fmt.Printf("Before Remove: Len=%d, Cap=%d, Data=%s\n", len(ms), cap(ms), ms) 
    ms.Remove(1) //remove element 1 (which also has a value of 1) 
    fmt.Printf("After Remove: Len=%d, Cap=%d, Data=%s\n", len(ms), cap(ms), ms) 
} 

a wyniki ...

Before Remove: Len=3, Cap=4, Data=[%!s(*main.myStruct=&{0}) %!s(*main.myStruct=&{1}) %!s(*main.myStruct=&{2})] 

Inside Remove = [%!s(*main.myStruct=&{0}) %!s(*main.myStruct=&{2})] 

After Remove: Len=3, Cap=4, Data=[%!s(*main.myStruct=&{0}) %!s(*main.myStruct=&{2}) %!s(*main.myStruct=&{2})] 

Odpowiedz

12

Miałeś rację po raz pierwszy z Remove1(). Remove pobiera kopię plasterka i dlatego nie może zmienić długości plasterka.

Problem w funkcji usuwania jest taki, że zgodnie z kolejnością operacji w Go, cięcie następuje przed zakończeniem dereferencji. To jest zmiana *slc = append(*slc[:item], *slc[item+1:]...) na *slc = append((*slc)[:item], (*slc)[item+1:]...).

Jednak polecam następujące dla czytelności i konserwacji:

func (slc *mySlice) Remove1(item int) { 
    s := *slc 
    s = append(s[:item], s[item+1:]...) 
    *slc = s 
} 
+0

Dziękuję bardzo za szybką odpowiedź Stephena! Tak więc, aby wyjaśnić, podczas przekazywania plasterków do funkcji/metod jedyną rzeczą, jaka kiedykolwiek zostanie przekazana, jest definicja struktury z 3 elementów (nigdy nie jest to podstawowa tablica). Przechodząc przez wartość przekazuję KOPIĘ definicji plastra, a przechodząc przez preferencję przekazuję wskaźnik do definicji plastra? Dzięki jeszcze raz! – user2736464

+0

Prawidłowo. Po przekazaniu wartości otrzymasz kopię len, czapkę i wskaźnik do tablicy podkładu. Po przejściu wskaźnika do plasterka można zmodyfikować len i zaślepkę oryginału. –

1

Ponieważ dołączać niekoniecznie zwracają ten sam adres odniesieniu do plasterka, jak Stephen Weinberg podkreślił. Innym sposobem obejścia tego ograniczenia jest zdefiniowanie struktury, która otacza plaster.

na przykład:

package main 

import "fmt" 

type IntList struct { 
    intlist []int 
} 

func (il *IntList) Pop() { 
    if len(il.intlist) == 0 { return } 
    il.intlist = il.intlist[:len(il.intlist)-1] 
} 

func (il *IntList) Add(i... int) { 
    il.intlist = append(il.intlist, i...) 
} 

func (il *IntList) String() string { 
    return fmt.Sprintf("%#v",il.intlist) 
} 

func main() { 
    intlist := &IntList{[]int{1,2,3}} 
    fmt.Println(intlist) 
    intlist.Pop() 
    fmt.Println(intlist) 
    intlist.Add([]int{4,5,6}...) 
    fmt.Println(intlist) 
} 

wyjściowa:

[]int{1, 2, 3} 
[]int{1, 2} 
[]int{1, 2, 4, 5, 6}