Est-ce que Go a une construction «if x in» similaire à Python?

289

Sans itérer sur l'ensemble du tableau, comment puis-je vérifier si xdans le tableau à l'aide de Go? La langue a-t-elle une construction?

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


la source
2
Pourrait être une dupe de stackoverflow.com/q/8307478/180100
7
AFAIK, Il n'y a pas de raccourci pour cela. En interne, python itère également sur le tableau, il n'y a pas de solution.
Danish94
6
BTW, une remarque importante à ce sujet est qu'il n'y a aucun moyen de le faire (comme vous le demandez) " [sans itération sur l'ensemble du tableau". Rendre ces boucles explicites (ou derrière une fonction telle que strings.Index) aide à rendre plus évident ce que fait le code. J'ai l'impression que peut-être vous pensez que Python in array:fait quelque chose de rapide / magique. AFAIK ce n'est pas. Rendre la boucle explicite permet de sensibiliser le rédacteur (et tous les lecteurs) et d'envisager d'autres implémentations (par exemple une carte).
Dave C
5
Cependant, si "x" dans l'ensemble est en effet très rapide.
Roberto Alsina
6
Je ne pense pas que nous demandions une approche programmatique rapide, nous demandons juste une approche concise (et nous n'avons toujours pas ...)
Migwell

Réponses:

340

Il n'y a pas d'opérateur intégré pour le faire dans Go. Vous devez parcourir le tableau. Vous pouvez écrire votre propre fonction pour le faire, comme ceci:

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

Si vous voulez pouvoir vérifier l'appartenance sans itérer sur toute la liste, vous devez utiliser une carte au lieu d'un tableau ou d'une tranche, comme ceci:

visitedURL := map[string]bool {
    "http://www.google.com": true,
    "https://paypal.com": true,
}
if visitedURL[thisSite] {
    fmt.Println("Already been here.")
}
andybalholm
la source
4
existe-t-il un moyen de le faire sans spécifier de type? disons que si je veux juste une fonction générale needleInHaystack (aiguille, botte de foin) sans méthodes distinctes pour chaque type
Allen
25
Cela pourrait être fait avec le paquet Reflect, mais ce serait assez inefficace (probablement aussi lent que si vous l'écriviez dans un langage dynamique comme Python). A part ça, non. C'est ce que les gens veulent dire quand ils disent que Go n'a pas de génériques.
andybalholm
2
Quiconque est tombé sur cette réponse doit noter que vous NE POUVEZ PAS trier les cartes. Gros inconvénient de l'utilisation des cartes go.
RisingSun
4
vous ne pouvez pas non plus trier les cartes (objets) en Javascript. Il s'agit d'un bogue v8 qui renvoie les valeurs des objets par ordre alphabétique.
kumarharsh
7
les cartes ne sont pas triées dans la plupart des langues - c'est la même chose pour le cours pour une structure de données de carte (hashmap).
Hejazzman
100

Une autre solution si la liste contient des valeurs statiques.

ex: vérification d'une valeur valide à partir d'une liste de valeurs valides:

func IsValidCategory(category string) bool {
    switch category {
    case
        "auto",
        "news",
        "sport",
        "music":
        return true
    }
    return false
}
sebest
la source
11
Ouais, et si vos "valeurs valides" proviennent d'une base de données?
Rami Dabain
Oui, c'est bien, mais seulement lorsque ces valeurs peuvent être définies à l'avance.
piggybox
2
@RonanDejhero alors je pourrais utiliser OERE: myValue IN (sous-requête) :)
anilech
3
C'est bien comparé à la meilleure réponse
piggybox
50

Ceci est une citation tirée du livre "Programming in Go: création d'applications pour le 21e siècle":

L'utilisation d'une recherche linéaire simple comme celle-ci est la seule option pour les données non triées et convient parfaitement aux petites tranches (jusqu'à des centaines d'éléments). Mais pour les tranches plus grandes, surtout si nous effectuons des recherches à plusieurs reprises, la recherche linéaire est très inefficace, nécessitant en moyenne la moitié des éléments à comparer à chaque fois.

Go fournit une méthode sort.Search () qui utilise l'algorithme de recherche binaire: cela nécessite la comparaison de seulement log2 (n) éléments (où n est le nombre d'éléments) à chaque fois. Pour mettre cela en perspective, une recherche linéaire de 1000000 éléments nécessite 500000 comparaisons en moyenne, avec le pire des cas de 1000000 comparaisons; une recherche binaire nécessite au plus 20 comparaisons, même dans le pire des cas.

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

AlexTT
la source
10
Cela n'a de sens que si vous effectuez des recherches répétées. Sinon, vous avez la complexité n * log (n) * log (n) pour le tri et la recherche binaire, contre seulement n pour la recherche linéaire.
Christian
1
C'est en fait juste n*log(n) + log(n), car ce sont deux opérations indépendantes conséquentes
pomo_mondreganto
27

L'exemple ci-dessus utilisant sort est proche, mais dans le cas de chaînes, utilisez simplement 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

Robert Weber
la source
2
Cette réponse ressemble à une copie moins informative de la réponse ci-dessous, qui s'est produite avant cette réponse.
cytinus
@cytinus De quelle réponse parlez-vous? C'est le seul sur lequel je vois sort.SearchStrings.
akim
1
J'ai obtenu une accélération de 100x lors de recherches répétées sur une grande tranche.
Xeoncross
20

Juste eu une question similaire et a décidé d'essayer certaines des suggestions de ce fil.

J'ai comparé les meilleurs et les pires scénarios de 3 types de recherche:

  • en utilisant une carte
  • en utilisant une liste
  • à l'aide d'une instruction switch

voici le code de fonction:

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
}

les meilleurs scénarios choisissent le premier élément dans les listes, les pires cas utilisent une valeur inexistante.

Voici les résultats:

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 gagne tout le chemin, le pire des cas est incroyablement plus rapide que le meilleur des cas. Les cartes sont les pires et la liste est plus proche de changer.

Donc, la morale est la suivante: si vous avez une liste statique et raisonnablement petite, la déclaration switch est la voie à suivre.

Igor
la source
Je ne sais pas si Go optimise ce cas, mais cela fait-il une différence si vous déplacez l'initialisation de la liste / carte en dehors de la fonction de test?
w00t le
Je suis curieux de voir comment la comparaison se déroulerait avec une liste triée et si le tri en valait la peine
Michael Draper
Qu'en est-il :au lieu de ,dans l'instruction switch? Cela accélère-t-il?
Thomas Sauvajon
J'ai essayé d'utiliser plusieurs casedéclarations au lieu d'un seul cas. Les résultats sont sensiblement les mêmes avec les deux fonctions.
Thomas Sauvajon
7

Une autre option consiste à utiliser une carte comme un ensemble. Vous utilisez uniquement les clés et la valeur doit être quelque chose comme un booléen qui est toujours vrai. Ensuite, vous pouvez facilement vérifier si la carte contient la clé ou non. Ceci est utile si vous avez besoin du comportement d'un ensemble, où si vous ajoutez une valeur plusieurs fois, elle n'est présente qu'une seule fois dans l'ensemble.

Voici un exemple simple où j'ajoute des nombres aléatoires comme clés d'une carte. Si le même nombre est généré plus d'une fois, cela n'a pas d'importance, il n'apparaîtra qu'une seule fois dans la carte finale. Ensuite, j'utilise une simple vérification si pour voir si une clé est dans la carte ou non.

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

Ici, c'est sur le terrain de jeu

noble
la source
0

C'est aussi proche que possible de la sensation naturelle de l'opérateur "in" de Python. Vous devez définir votre propre type. Ensuite, vous pouvez étendre les fonctionnalités de ce type en ajoutant une méthode comme "has" qui se comporte comme vous l'espérez.

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

J'ai une bibliothèque d'utilitaires où je définis quelques éléments communs comme celui-ci pour plusieurs types de tranches, comme celles contenant des entiers ou mes propres autres structures.

Oui, cela fonctionne en temps linéaire, mais ce n'est pas le but. Le but est de demander et d'apprendre quelles constructions de langage communes Go a et n'a pas. C'est un bon exercice. Que cette réponse soit stupide ou utile dépend du lecteur.

IcarusNM
la source