2016-07-15 5 views
2

Je veux supprimer certains éléments d'une tranche, et https://github.com/golang/go/wiki/SliceTricks conseille cette tranche de manipulation:golang supprimer des éléments lorsque vous parcourez tranche paniques

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

Je codé ci-dessous:

package main 

import (
    "fmt" 
) 

func main() { 
    slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} 
    for i, value := range slice { 
     if value%3 == 0 { // remove 3, 6, 9 
      slice = append(slice[:i], slice[i+1:]...) 
     } 
    } 
    fmt.Printf("%v\n", slice) 
} 

avec go run hello.go, il panique:

panic: runtime error: slice bounds out of range 

goroutine 1 [running]: 
panic(0x4ef680, 0xc082002040) 
    D:/Go/src/runtime/panic.go:464 +0x3f4 
main.main() 
    E:/Code/go/test/slice.go:11 +0x395 
exit status 2 

Comment puis-je modifier ce code pour obtenir les droits?

J'ai essayé ci-dessous:

1er, avec une déclaration goto:

func main() { 
    slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} 
Label: 
    for i, n := range slice { 
     if n%3 == 0 { 
      slice = append(slice[:i], slice[i+1:]...) 
      goto Label 
     } 
    } 
    fmt.Printf("%v\n", slice) 
} 

cela fonctionne, mais trop itération

2, utilisez une autre tranche de partage même tableau de support:

func main() { 
    slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} 
    dest := slice[:0] 
    for _, n := range slice { 
     if n%3 != 0 { // filter 
      dest = append(dest, n) 
     } 
    } 
    slice = dest 
    fmt.Printf("%v\n", slice) 
} 

mais je ne sais pas si celui-ci c'est mieux ou pas.

3, de Remove elements in slice, avec len opérateur:

func main() { 
    slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} 
    for i := 0; i < len(slice); i++ { 
     if slice[i]%3 == 0 { 
      slice = append(slice[:i], slice[i+1:]...) 
      i-- // should I decrease index here? 
     } 
    } 
    fmt.Printf("%v\n", slice) 
} 

qui dois-je prendre maintenant?

avec référence:

func BenchmarkRemoveSliceElementsBySlice(b *testing.B) { 
    for i := 0; i < b.N; i++ { 
     slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} 
     dest := slice[:0] 
     for _, n := range slice { 
      if n%3 != 0 { 
       dest = append(dest, n) 
      } 
     } 
    } 
} 

func BenchmarkRemoveSliceElementByLen(b *testing.B) { 
    for i := 0; i < b.N; i++ { 
     slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9} 
     for i := 0; i < len(slice); i++ { 
      if slice[i]%3 == 0 { 
       slice = append(slice[:i], slice[i+1:]...) 
      } 
     } 
    } 
} 


$ go test -v -bench=".*" 
testing: warning: no tests to run 
PASS 
BenchmarkRemoveSliceElementsBySlice-4 50000000    26.6 ns/op 
BenchmarkRemoveSliceElementByLen-4  50000000    32.0 ns/op 

il semble supprimer tous les éléments en une seule boucle est mieux

Répondre

6

itérer sur les éléments de copie de tranche que vous souhaitez conserver.

k := 0 
for _, n := range slice { 
    if n%3 != 0 { // filter 
     slice[k] = n 
     k++ 
    } 
} 
slice = slice[:k] // set slice len to remaining elements 

L'astuce de découpe est utile dans le cas où un seul élément est supprimé. S'il est possible que plus d'un élément soit supprimé, utilisez la boucle for ci-dessus.

working playground example

+0

En plus de cette réponse, il est plus efficace de itérer sur l'ensemble du réseau comme celui-ci et remplir un nouveau. Si vous utilisez des primitives simples ici, vous obtiendrez un gain de performances matérielles avec prédiction.Lire sur "Sympathie mécanique" sur le codage, en particulier dans Go, pour tirer parti des algorithmes du processeur. Bien que votre benchmark ne montre pas beaucoup de gains avec 9 numéros, essayez quelques millions pour voir les gains. – eduncan911

1

tout cela est une bonne réponse pour petite tranche:

package main 

import "fmt" 

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

    k := 0 
    for _, n := range slice { 
     if n%3 != 0 { // filter 
      slice[k] = n 
      k++ 
     } 
    } 
    slice = slice[:k] 

    fmt.Println(slice) //[1 2 4 5 7 8] 
} 

pour minimiser écriture mémoire pour les premiers éléments (pour grosse tranche), vous pouvez utiliser ceci:

package main 

import "fmt" 

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

    k := 0 
    for i, n := range slice { 
     if n%3 != 0 { // filter 
      if i != k { 
       slice[k] = n 
      } 
      k++ 
     } 
    } 
    slice = slice[:k] 

    fmt.Println(slice) //[1 2 4 5 7 8] 
} 

et si vous avez besoin nouvelle tranche ou préservation ancienne tranche:

package main 

import "fmt" 

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

    s2 := make([]int, len(slice)) 
    k := 0 
    for _, n := range slice { 
     if n%3 != 0 { // filter 
      s2[k] = n 
      k++ 
     } 
    } 
    s2 = s2[:k] 

    fmt.Println(s2) //[1 2 4 5 7 8] 
}