2014-07-14 10 views
5

Desidero eliminare elementi da una raccolta in un modo sul posto. Si consideri il seguente frammento di codice:Eliminazione in posto degli elementi della sezione Golang

package main 

import "fmt" 

type Ints []int 

func (xs Ints) Filter() { 
    for i := 0; i < len(xs); i++ { 
     if xs[i]%2 == 0 { // Or some other filtering function 
      xs = append(xs[:i], xs[i+1:]...) 
     } 
     fmt.Printf("i %+v\n", i) 
     fmt.Printf("xs %+v\n", xs) 
    } 
} 

func main() { 
    a := Ints([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) 

    fmt.Printf("initial a %+v\n", a) 

    a.Filter() 

    fmt.Printf("final a %+v\n", a) 
} 

http://play.golang.org/p/1nL6Il2Gf1

Il risultato sorprendente è: final a [1 3 5 7 9 10 10 10 10 10]

mi chiedo come fare questo. Sono abbastanza sicuro che il ricevitore debba essere un puntatore a Ints. Ma ciò incasina un po 'il codice (aggiungendo *xs ovunque possibilmente con parentesi) ma, soprattutto, produce lo stesso risultato.

risposta

2

Lo farei spostando gli elementi, quindi ridimensionando la sezione e utilizzando un puntatore. Qualcosa del genere:

package main 

import "fmt" 

type Ints []int 

func (xs *Ints) Filter() { 
    filterPos := 0 
    for i := 0; i < len(*xs); i++ { 
     if (*xs)[i]%2 == 0 { // Or some other filtering function 
      (*xs)[filterPos] = (*xs)[i] 
     filterPos++ 
     } 
    } 
    (*xs) = (*xs)[:filterPos] 
} 

func main() { 
    a := Ints([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) 

    fmt.Printf("initial a %+v\n", a) 

    a.Filter() 

    fmt.Printf("final a %+v\n", a) 
} 
+1

Solo una piccola raccomandazione, l'uso '(* xs) = (* xs) [: filterPos: filterPos]' invece di tagliare la fetta e libera la memoria aggiuntiva, potrebbe essere un dignitoso quantità di memoria con oggetti più grandi. – OneOfOne

+0

@OneOfOne true, dipende se si sta ottimizzando la velocità o la memoria. In realtà creerei solo una nuova sezione e manterrai tutto immutabile, a meno che non si tratti di un'enorme quantità di memoria. –

+0

Vero, ecco cosa intendevo http://play.golang.org/p/FgeuFdr3-v – OneOfOne

1

È necessario utilizzare il puntatore qui. Se non ti piace aggiungere * xs ogni dove basta usare una variabile temporanea per fare tutte le operazioni, quindi reimpostare. Ecco il codice http://play.golang.org/p/eAFkV3Lwh6

1

Il tuo codice era quasi corretto.

Il primo bug è che è necessario evitare di fare i++ quando si elimina un elemento, altrimenti lo i++ salta il successivo elemento non letto. Ecco perché l'ho inserito nella clausola else.

Il secondo bug è che xs è una variabile locale della funzione Filter, quindi se si cambia ciò a cui punta (con xs = ...) che non cambia ciò che a punti verso l'interno main. Puoi risolvere questo problema rendendolo un puntatore (*xs come altri hanno pubblicato) o restituendo la nuova slice filtrata, come ho fatto di seguito.

package main 

import "fmt" 

type Ints []int 

func (xs Ints) Filtered() Ints { 
    for i := 0; i < len(xs); { 
     if xs[i]%2 == 0 { 
      xs = append(xs[:i], xs[i+1:]...) 
     } else { 
      i++ 
     } 
    } 
    return xs 
} 

func main() { 
    a := Ints([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) 
    b := a.Filtered() 
    fmt.Println(b) 
} 

http://play.golang.org/p/Nre7w4KQ78

Problemi correlati