Go ha un costrutto "if x in" simile a Python?


289

Senza iterare sull'intero array, come posso verificare se xnell'array usando Go? La lingua ha un costrutto?

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


2
Potrebbe essere una vittima di stackoverflow.com/q/8307478/180100

7
AFAIK, Non c'è scorciatoia per quello in corso. Internamente, Python scorre anche sull'array, non c'è nulla da fare.
Danish94

6
A proposito, una nota importante per questo è che non c'è modo di farlo (come si chiede) " [senza] iterare sull'intero array". Rendere espliciti tali loop (o dietro una funzione come strings.Index) aiuta a rendere più ovvio ciò che il codice sta facendo. Ho l'impressione che forse pensi che Python in array:stia facendo qualcosa di veloce / magico. AFAIK non lo è. Rendere esplicito il ciclo aiuta a rendere lo scrittore (e tutti i lettori) consapevole e prendere in considerazione altre implementazioni (ad esempio una mappa).
Dave C,

5
Tuttavia, se "x" nel set è davvero molto veloce.
Roberto Alsina,

6
Non credo che stiamo chiedendo un approccio programmaticamente veloce, ne stiamo solo chiedendo uno conciso (e ancora non abbiamo ...)
Migwell,

Risposte:


340

Non esiste un operatore integrato per farlo in Go. È necessario iterare sull'array. Puoi scrivere la tua 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 vuoi essere in grado di verificare l'appartenenza senza iterare l'intero elenco, devi utilizzare una mappa anziché un array o una sezione, in questo modo:

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

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

25
Potrebbe essere fatto con il pacchetto riflesso, ma sarebbe abbastanza inefficiente (probabilmente lento quanto se lo scrivessi in un linguaggio dinamico come Python). A parte questo, no. Questo è ciò che la gente intende quando dicono che Go non ha generici.
Andybalholm,

2
Chiunque si imbattesse in questa risposta deve notare che NON PUOI ordinare le mappe. Grande svantaggio dell'uso delle mappe go.
RisingSun

4
non è possibile ordinare le mappe (oggetti) anche in Javascript. È un bug v8 che gli oggetti restituiscono valori in ordine alfabetico.
Kumarharsh,

7
le mappe non sono ordinate nella maggior parte delle lingue: è la norma del corso per una struttura di dati della mappa (hashmap).
Hejazzman,

100

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

ad es .: 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
}

11
Sì, e se i tuoi "valori validi" provengono da un database?
Rami Dabain,

Sì, questo è pulito ma solo quando questi valori possono essere definiti in anticipo.
salvadanaio

2
@RonanDejhero allora potrei usare DOVE: myValue IN (subquery) :)
anilech

3
Questo è pulito rispetto alla risposta migliore
piggybox

50

Questa è la citazione dal libro "Programmazione in corso: creazione di applicazioni per il 21 ° secolo":

L'uso di una semplice ricerca lineare come questa è l'unica opzione per i dati non ordinati e va bene per le piccole porzioni (fino a centinaia di elementi). Ma per sezioni più grandi, specialmente se eseguiamo ripetutamente ricerche, 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 di solo log2 (n) elementi (dove n è il numero di elementi) ogni volta. Per mettere questo in prospettiva, una ricerca lineare di 1000000 articoli richiede in media 500000 confronti, con il caso peggiore di 1000000 confronti; una ricerca binaria richiede al massimo 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


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

1
In realtà è solo n*log(n) + log(n), poiché sono due operazioni indipendenti conseguenti
pomo_mondreganto il

27

L'esempio sopra usando sort è vicino, ma nel caso di stringhe usa semplicemente 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


2
Questa risposta sembra una copia meno istruttiva della risposta di seguito, che è avvenuta prima di questa risposta.
Citino,

@cytinus A quale risposta ti riferisci? Questo è l'unico su cui vedo basato sort.SearchStrings.
Akim

1
Ho ottenuto uno speedup di 100x su ricerche ripetute attraverso una grande fetta.
Xeoncross,

20

Ho appena avuto una domanda simile e ho deciso di provare alcuni dei suggerimenti in questa discussione.

Ho confrontato scenari migliori e peggiori di 3 tipi di ricerca:

  • usando una mappa
  • usando un elenco
  • usando un'istruzione switch

ecco il codice funzione:

func belongsToMap(lookup string) bool {
list := map[string]bool{
    "900898296857": true,
    "900898302052": true,
    "900898296492": true,
    "900898296850": true,
    "900898296703": true,
    "900898296633": true,
    "900898296613": true,
    "900898296615": true,
    "900898296620": true,
    "900898296636": true,
}
if _, ok := list[lookup]; ok {
    return true
} else {
    return false
}
}


func belongsToList(lookup string) bool {
list := []string{
    "900898296857",
    "900898302052",
    "900898296492",
    "900898296850",
    "900898296703",
    "900898296633",
    "900898296613",
    "900898296615",
    "900898296620",
    "900898296636",
}
for _, val := range list {
    if val == lookup {
        return true
    }
}
return false
}

func belongsToSwitch(lookup string) bool {
switch lookup {
case
    "900898296857",
    "900898302052",
    "900898296492",
    "900898296850",
    "900898296703",
    "900898296633",
    "900898296613",
    "900898296615",
    "900898296620",
    "900898296636":
    return true
}
return false
}

gli scenari migliori selezionano il primo elemento negli elenchi, quelli peggiori usano un valore inesistente.

ecco i risultati:

BenchmarkBelongsToMapWorstCase-4 2000000 787 ns/op BenchmarkBelongsToSwitchWorstCase-4 2000000000 0.35 ns/op BenchmarkBelongsToListWorstCase-4 100000000 14.7 ns/op BenchmarkBelongsToMapBestCase-4 2000000 683 ns/op BenchmarkBelongsToSwitchBestCase-4 100000000 10.6 ns/op BenchmarkBelongsToListBestCase-4 100000000 10.4 ns/op

Switch vince fino in fondo, il caso peggiore è sorprendentemente più veloce del caso migliore. Le mappe sono le peggiori e l'elenco è più vicino al passaggio.

Quindi la morale è: se hai un elenco statico, ragionevolmente piccolo, l'opzione switch è la strada da percorrere.


Non so se Go ottimizzi questo caso, ma fa differenza se si sposta l'inizializzazione dell'elenco / della mappa fuori dalla funzione di test?
wt

Sono curioso di vedere come si comporterebbe il confronto con un elenco ordinato e se ne varrebbe la pena
Michael Draper,

Che dire :invece di ,nell'istruzione switch? Lo rende più veloce?
Thomas Sauvajon,

Ho provato a utilizzare più caseistruzioni anziché un singolo caso. I risultati sono sensibilmente gli stessi con entrambe le funzioni.
Thomas Sauvajon,

7

Un'altra opzione sta usando 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à nella mappa finale solo una volta. Quindi uso un semplice controllo if 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)
        }
    }
}

Eccolo nel parco giochi


0

Questo è il più vicino possibile alla sensazione naturale dell'operatore "in" di Python. Devi definire il tuo tipo. Quindi è possibile estendere la funzionalità di quel tipo aggiungendo un metodo come "has" che si comporta come si spera.

package main

import "fmt"

type StrSlice []string

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

func main() {
    var testList = StrSlice{"The", "big", "dog", "has", "fleas"}

    if testList.Has("dog") {
        fmt.Println("Yay!")
    }
}

Ho una libreria di utilità in cui definisco alcune cose comuni come questa per diversi tipi di sezioni, come quelle che contengono numeri interi o altre mie strutture.

Sì, funziona in tempo lineare, ma non è questo il punto. Il punto è chiedere e imparare quali costrutti linguistici comuni hanno e non hanno Go. È un buon esercizio. Se questa risposta è sciocca o utile dipende dal lettore.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.