Hat Go ein "if x in" -Konstrukt ähnlich wie Python?


289

Wie kann ich überprüfen, ob sich das xArray mit Go befindet, ohne das gesamte Array zu durchlaufen? Hat die Sprache ein Konstrukt?

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


2
Könnte ein Betrüger von stackoverflow.com/q/8307478/180100

7
AFAIK, dafür gibt es keine Abkürzung. Intern iteriert Python auch über das Array, daran führt kein Weg vorbei.
Danish94

6
Übrigens ist ein wichtiger Hinweis dazu, dass es keine Möglichkeit gibt , dies zu tun (wie Sie es wünschen ) " [ohne] über das gesamte Array zu iterieren". Wenn Sie solche Schleifen explizit (oder hinter einer Funktion wie strings.Index) machen, wird klarer, was der Code tut. Ich habe den Eindruck, dass Sie vielleicht denken, Pythons in array:macht etwas schnelles / magisches. AFAIK ist es nicht. Wenn Sie die Schleife explizit machen, wird der Verfasser (und alle Leser) darauf aufmerksam gemacht und andere Implementierungen (z. B. eine Karte) berücksichtigt.
Dave C

5
Wenn jedoch "x" im Satz tatsächlich sehr schnell ist.
Roberto Alsina

6
Ich glaube nicht, dass wir nach einem programmatisch schnellen Ansatz fragen, wir fragen nur nach einem prägnanten (und haben immer noch nicht ...)
Migwell

Antworten:


340

In Go gibt es keinen integrierten Operator. Sie müssen über das Array iterieren. Sie können dazu Ihre eigene Funktion schreiben:

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

Wenn Sie nach einer Mitgliedschaft suchen möchten, ohne die gesamte Liste zu durchlaufen, müssen Sie eine Karte anstelle eines Arrays oder Slice wie folgt verwenden:

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

4
Gibt es eine Möglichkeit, dies ohne Angabe des Typs zu tun? Nehmen wir an, ich möchte nur eine allgemeine NeedleInHaystack-Funktion (Nadel, Heuhaufen) ohne separate Methoden für jeden einzelnen Typ
Allen,

25
Es könnte mit dem Reflect-Paket gemacht werden, aber es wäre ziemlich ineffizient (wahrscheinlich so langsam, als ob Sie es in einer dynamischen Sprache wie Python geschrieben hätten). Davon abgesehen nein. Das meinen die Leute, wenn sie sagen, dass Go keine Generika hat.
Andybalholm

2
Jeder, der auf diese Antwort gestoßen ist, muss beachten, dass Sie keine Karten sortieren können. Großer Nachteil bei der Verwendung von Go-Karten.
RisingSun

4
Sie können Karten (Objekte) auch nicht in Javascript sortieren. Es ist ein v8- Fehler, dass Objekte Werte in alphabetisch sortierter Reihenfolge zurückgeben.
Kumarshar

7
Karten sind in den meisten Sprachen nicht sortiert - das ist für eine Kartendatenstruktur (Hashmap) selbstverständlich.
Hejazzman

100

Eine andere Lösung, wenn die Liste statische Werte enthält.

Beispiel: Überprüfen eines gültigen Werts aus einer Liste gültiger Werte:

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

11
Ja, was ist, wenn Ihre "gültigen Werte" aus einer Datenbank stammen?
Rami Dabain

Ja, das ist ordentlich, aber nur, wenn diese Werte im Voraus definiert werden können.
Piggybox

2
@ RonanDejhero dann könnte ich verwenden, wo: myValue IN (Unterabfrage) :)
Anilech

3
Dies ist ordentlich im Vergleich zu der Top-Antwort
Piggybox

50

Dies ist ein Zitat aus dem Buch "Programming in Go: Erstellen von Anwendungen für das 21. Jahrhundert":

Die Verwendung einer einfachen linearen Suche wie dieser ist die einzige Option für unsortierte Daten und eignet sich für kleine Segmente (bis zu Hunderte von Elementen). Bei größeren Slices - insbesondere wenn wir wiederholt suchen - ist die lineare Suche jedoch sehr ineffizient, da im Durchschnitt jedes Mal die Hälfte der Elemente verglichen werden muss.

Go bietet eine sort.Search () -Methode, die den binären Suchalgorithmus verwendet: Dies erfordert den Vergleich von nur log2 (n) -Elementen (wobei n die Anzahl der Elemente ist) jedes Mal. Um dies ins rechte Licht zu rücken, erfordert eine lineare Suche von 1000000 Elementen durchschnittlich 500000 Vergleiche, im schlimmsten Fall 1000000 Vergleiche. Eine binäre Suche benötigt selbst im schlimmsten Fall höchstens 20 Vergleiche.

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
Dies ist nur sinnvoll, wenn Sie wiederholt suchen. Andernfalls haben Sie die Komplexität n * log (n) * log (n) für die Sortier- und Binärsuche gegenüber nur n für die lineare Suche.
Christian

1
Es ist eigentlich nur so n*log(n) + log(n), wie es zwei
aufeinanderfolgende

27

Das obige Beispiel mit sort ist naheliegend, aber bei Zeichenfolgen verwenden Sie einfach 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
Diese Antwort sieht aus wie eine weniger informative Kopie der folgenden Antwort, die vor dieser Antwort stattgefunden hat.
Cytinus

@cytinus Auf welche Antwort beziehen Sie sich? Das ist der einzige, auf dem ich sehe sort.SearchStrings.
Akim

1
Bei wiederholten Suchen in einem großen Slice wurde die Geschwindigkeit um das 100-fache erhöht.
Xeoncross

20

Hatte gerade eine ähnliche Frage und beschloss, einige der Vorschläge in diesem Thread auszuprobieren.

Ich habe die besten und schlechtesten Szenarien für drei Arten der Suche verglichen:

  • mit einer Karte
  • mit einer Liste
  • mit einer switch-Anweisung

Hier ist der Funktionscode:

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
}

In Best-Case-Szenarien wird das erste Element in Listen ausgewählt, in Worst-Case-Szenarien wird ein nicht vorhandener Wert verwendet.

Hier sind die Ergebnisse:

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 gewinnt den ganzen Weg, der schlimmste Fall ist überaus schneller als der beste Fall. Karten sind die schlechtesten und die Liste ist näher am Umschalten.

Die Moral lautet also: Wenn Sie eine statische, relativ kleine Liste haben, ist die switch-Anweisung der richtige Weg.


Ich weiß nicht, ob Go diesen Fall optimiert, aber macht es einen Unterschied, wenn Sie die Initialisierung der Liste / Karte außerhalb der Testfunktion verschieben?
w00t

Ich bin gespannt, wie sich der Vergleich mit einer sortierten Liste entwickeln würde und ob sich die Sortierung lohnen würde
Michael Draper

Was ist :statt ,in der switch-Anweisung? Macht es schneller?
Thomas Sauvajon

Ich habe versucht, mehrere caseAnweisungen anstelle eines Einzelfalls zu verwenden. Die Ergebnisse sind bei beiden Funktionen vernünftigerweise gleich.
Thomas Sauvajon

7

Eine andere Option ist die Verwendung einer Karte als Set. Sie verwenden nur die Schlüssel und der Wert ist so etwas wie ein Boolescher Wert, der immer wahr ist. Dann können Sie leicht überprüfen, ob die Karte den Schlüssel enthält oder nicht. Dies ist nützlich, wenn Sie das Verhalten eines Satzes benötigen. Wenn Sie einen Wert mehrmals hinzufügen, ist er nur einmal im Satz enthalten.

Hier ist ein einfaches Beispiel, in dem ich Zufallszahlen als Schlüssel zu einer Karte hinzufüge. Wenn dieselbe Nummer mehrmals generiert wird, spielt dies keine Rolle, sie wird nur einmal in der endgültigen Karte angezeigt. Dann benutze ich eine einfache if-Prüfung, um festzustellen, ob sich ein Schlüssel in der Karte befindet oder nicht.

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)
        }
    }
}

Hier ist es unterwegs Spielplatz


0

Dies ist so nah wie möglich am natürlichen Gefühl von Pythons "in" -Operator. Sie müssen Ihren eigenen Typ definieren. Dann können Sie die Funktionalität dieses Typs erweitern, indem Sie eine Methode wie "has" hinzufügen, die sich so verhält, wie Sie es sich erhoffen.

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!")
    }
}

Ich habe eine Dienstprogrammbibliothek, in der ich einige allgemeine Dinge wie diese für verschiedene Arten von Slices definiere, z. B. solche, die Ganzzahlen oder meine eigenen anderen Strukturen enthalten.

Ja, es läuft in linearer Zeit, aber das ist nicht der Punkt. Es geht darum zu fragen und zu lernen, welche gängigen Sprachkonstrukte Go hat und welche nicht. Es ist eine gute Übung. Ob diese Antwort albern oder nützlich ist, liegt beim Leser.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.