2013-03-10 6 views
153

Senza iterazione su tutto l'array come posso controllare se 'x' in array in Vai?Golang ha costrutto "if x in" simile a Python?

come Python: if "x" in array: ...

+2

potrebbe essere una vittima di http://stackoverflow.com/q/8307478/180100 –

+4

per quanto ne so, non c'è scorciatoia per quella in movimento. Internamente, python esegue anche un'iterazione sull'array, non è possibile aggirarlo. – Danish94

+3

BTW, una nota importante a questo è che non è * assolutamente * farlo (come chiedete) "** [w] senza ** iterare sull'intero array". Rendere tali loop espliciti (o dietro una funzione come 'strings.Index') aiuta a rendere più ovvio ciò che sta facendo il codice. Ho l'impressione che forse tu pensi che Python sia "in array:" sta facendo qualcosa di veloce/magico. AFAIK non lo è. Rendere esplicito il ciclo aiuta a rendere lo scrittore (e tutti i lettori) consapevoli e considera altre implementazioni (ad esempio una mappa). –

risposta

197

Non v'è alcun built-in operatore di farlo in Go. È necessario scorrere l'array. È possibile scrivere la propria funzione per farlo, in questo modo:

func stringInSlice(a string, list []string) bool { 
    for _, b := range list { 
     if b == a { 
      return true 
     } 
    } 
    return false 
} 

Se si vuole essere in grado di verificare la presenza di appartenenza, senza l'iterazione su tutta la lista, è necessario utilizzare una mappa al posto di un array o slice, in questo modo:

visitedURL := map[string]bool { 
    "http://www.google.com": true, 
    "https://paypal.com": true, 
} 
if visitedURL[thisSite] { 
    fmt.Println("Already been here.") 
} 
+1

c'è un modo per farlo senza specificare il tipo? diciamo se voglio solo una funzione generale needleInHaystack (ago, pagliaio) senza metodi separati per ogni singolo tipo – Allen

+9

Potrebbe essere fatto con il pacchetto reflect, ma sarebbe abbastanza inefficiente (probabilmente è lento come se lo scriveste in un linguaggio dinamico come Python). Oltre a questo, no. Questo è ciò che le persone intendono quando dicono che Go non ha generici. – andybalholm

+1

Chiunque si sia imbattuto in questa risposta deve notare che NON PUOI ordinare le mappe. Grande vantaggio nell'utilizzo delle mappe go. – khuderm

34

questo è citazione dal libro "Programmazione in Go: creazione di applicazioni per il 21 ° secolo":

Utilizzando una semplice ricerca lineare come questo è l'unica opzione per indifferenziati dati un e va bene per piccole fette (fino a centinaia di articoli). Ma per le porzioni più grandi di , specialmente se eseguiamo ricerche ripetutamente, la ricerca lineare è molto inefficiente, in media richiede la metà degli articoli da confrontare ogni volta.

Go fornisce un metodo sort.Search(), che utilizza l'algoritmo di ricerca binaria : questo richiede il confronto del solo log2 (n) articoli (dove n è il numero di elementi) di volta in volta. Per mettere questo in prospettiva, una ricerca lineare di 1000000 elementi richiede 500000 confronti in media, con il caso peggiore di 1000000 confronti; una ricerca binaria necessita di la maggior parte dei 20 confronti, anche nel caso peggiore.

files := []string{"Test.conf", "util.go", "Makefile", "misc.go", "main.go"} 
target := "Makefile" 
sort.Strings(files) 
i := sort.Search(len(files), 
    func(i int) bool { return files[i] >= target }) 
if i < len(files) && files[i] == target { 
    fmt.Printf("found \"%s\" at files[%d]\n", files[i], i) 
} 

https://play.golang.org/p/UIndYQ8FeW

+2

Questo ha senso solo se si effettuano ricerche ripetute. Altrimenti hai la complessità n * log (n) * log (n) per l'ordinamento e la ricerca binaria, contro solo n per la ricerca lineare. – christian

40

Un'altra soluzione, se l'elenco contiene valori statici.

esempio: controllo di un valore valido da un elenco di valori validi:

func IsValidCategory(category string) bool { 
    switch category { 
    case 
     "auto", 
     "news", 
     "sport", 
     "music": 
     return true 
    } 
    return false 
} 
+1

Sì, e se i tuoi "valori validi" provengono da un database? –

14

L'esempio utilizzando ordinamento è vicino, ma nel caso di stringhe sufficiente utilizzare SearchString:

files := []string{"Test.conf", "util.go", "Makefile", "misc.go", "main.go"} 
target := "Makefile" 
sort.Strings(files) 
i := sort.SearchStrings(files, target) 
if i < len(files) && files[i] == target { 
    fmt.Printf("found \"%s\" at files[%d]\n", files[i], i) 
} 

https://golang.org/pkg/sort/#SearchStrings

+0

Questa risposta appare come una copia meno informativa della risposta di seguito, che è accaduto prima di questa risposta. – cytinus

+0

@cytinus A quale risposta ti riferisci? Questo è l'unico che vedo basato su 'sort.SearchStrings'. – akim

+0

Ho ottenuto un aumento di velocità 100x per ricerche ripetute attraverso una grande fetta. – Xeoncross

5

Un'altra opzione è l'utilizzo di una mappa come set. Usi solo le chiavi e il valore è qualcosa di simile a un booleano che è sempre vero. Quindi puoi facilmente verificare se la mappa contiene o meno la chiave. Questo è utile se hai bisogno del comportamento di un set, dove se aggiungi un valore più volte è solo nel set una volta.

Ecco un semplice esempio in cui aggiungo numeri casuali come chiavi a una mappa. Se lo stesso numero viene generato più di una volta, non importa, apparirà solo una volta nella mappa finale. Quindi uso un semplice controllo per vedere se una chiave è nella mappa o no.

package main 

import (
    "fmt" 
    "math/rand" 
) 

func main() { 
    var MAX int = 10 

    m := make(map[int]bool) 

    for i := 0; i <= MAX; i++ { 
     m[rand.Intn(MAX)] = true 
    } 

    for i := 0; i <= MAX; i++ { 
     if _, ok := m[i]; ok { 
      fmt.Printf("%v is in map\n", i) 
     } else { 
      fmt.Printf("%v is not in map\n", i) 
     } 
    } 
} 

Here it is on the go playground

Problemi correlati