¿Go tiene una construcción "if x in" similar a Python?

289

Sin iterar sobre toda la matriz, ¿cómo puedo verificar si estoy xen la matriz usando Go? ¿El lenguaje tiene una construcción?

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


fuente
2
Podría ser un engañado de stackoverflow.com/q/8307478/180100
77
AFAIK, no hay taquigrafía para eso en marcha. Internamente, Python también itera sobre la matriz, no hay forma de evitarlo.
Danish94
66
Por cierto, una nota importante para esto es que no hay forma de hacer esto (como usted lo solicita) " [sin] iterar sobre toda la matriz". Hacer explícitos estos bucles (o detrás de una función como strings.Index) ayuda a que sea más obvio lo que está haciendo el código. Tengo la impresión de que quizás piensas que Python in array:está haciendo algo rápido / mágico. AFAIK no lo es. Hacer explícito el bucle ayuda a que el escritor (y todos los lectores) conozcan y consideren otras implementaciones (por ejemplo, un mapa).
Dave C
55
Sin embargo, si "x" en conjunto es realmente muy rápido.
Roberto Alsina
66
No creo que estemos pidiendo un enfoque programáticamente rápido, solo estamos pidiendo uno conciso (y aún no lo hemos conseguido ...)
Migwell

Respuestas:

340

No hay un operador integrado para hacerlo en Go. Necesita iterar sobre la matriz. Puede escribir su propia función para hacerlo, así:

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

Si desea poder verificar la membresía sin iterar sobre la lista completa, debe usar un mapa en lugar de una matriz o un segmento, como este:

visitedURL := map[string]bool {
    "http://www.google.com": true,
    "https://paypal.com": true,
}
if visitedURL[thisSite] {
    fmt.Println("Already been here.")
}
andybalholm
fuente
44
¿Hay alguna manera de hacer esto sin especificar el tipo? digamos si quiero solo una función general needleInHaystack (aguja, pajar) sin métodos separados para cada tipo
Allen
25
Podría hacerse con el paquete reflect, pero sería bastante ineficiente (probablemente tan lento como si lo escribiera en un lenguaje dinámico como Python). Aparte de eso, no. A eso se refiere la gente cuando dice que Go no tiene genéricos.
andybalholm
2
Cualquiera que se haya topado con esta respuesta debe tener en cuenta que NO PUEDE ordenar los mapas. Gran inconveniente de usar go maps.
RisingSun
44
no puedes ordenar mapas (objetos) en Javascript también. Es un error v8 que los objetos devuelven valores en orden alfabético.
kumarharsh
77
los mapas no están ordenados en la mayoría de los idiomas, eso es normal para el curso de una estructura de datos de mapa (hashmap).
Hejazzman
100

Otra solución si la lista contiene valores estáticos.

por ejemplo: verificar un valor válido de una lista de valores válidos:

func IsValidCategory(category string) bool {
    switch category {
    case
        "auto",
        "news",
        "sport",
        "music":
        return true
    }
    return false
}
sebest
fuente
11
Sí, ¿y si sus "valores válidos" provienen de una base de datos?
Rami Dabain
Sí, esto es bueno, pero solo cuando estos valores se pueden definir de antemano.
piggybox
2
@RonanDejhero, entonces podría usar WHERE: myValue IN (subconsulta) :)
anilech
3
Esto es bueno en comparación con la respuesta principal
piggybox 03 de
50

Esta es una cita del libro "Programación en marcha: creación de aplicaciones para el siglo XXI":

El uso de una búsqueda lineal simple como esta es la única opción para datos sin clasificar y está bien para pequeñas porciones (hasta cientos de elementos). Pero para los sectores más grandes, especialmente si estamos realizando búsquedas repetidas veces, la búsqueda lineal es muy ineficiente y, en promedio, requiere que se comparen la mitad de los elementos cada vez.

Go proporciona un método sort.Search () que utiliza el algoritmo de búsqueda binaria: esto requiere la comparación de solo elementos log2 (n) (donde n es el número de elementos) cada vez. Para poner esto en perspectiva, una búsqueda lineal de 1000000 artículos requiere 500000 comparaciones en promedio, con el peor de los casos de 1000000 comparaciones; Una búsqueda binaria necesita como máximo 20 comparaciones, incluso en el peor de los casos.

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
fuente
10
Esto solo tiene sentido si realiza búsquedas repetidas. De lo contrario, tendrá la complejidad n * log (n) * log (n) para la búsqueda de clasificación y binaria, en comparación con solo n para la búsqueda lineal.
cristiano
1
En realidad es justo n*log(n) + log(n), ya que son dos operaciones independientes consecuentes
pomo_mondreganto
27

El ejemplo anterior usando sort está cerca, pero en el caso de cadenas simplemente use 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
fuente
2
Esta respuesta parece una copia de pegar menos informativa de la respuesta a continuación, que sucedió antes de esta respuesta.
cytinus
@cytinus ¿A qué respuesta te refieres? Ese es el único en el que veo basado sort.SearchStrings.
akim
1
Obtuve una aceleración de 100x en búsquedas repetidas a través de una gran porción.
Xeoncross
20

Simplemente tuve una pregunta similar y decidí probar algunas de las sugerencias en este hilo.

He comparado los mejores y peores escenarios de 3 tipos de búsqueda:

  • usando un mapa
  • usando una lista
  • usando una declaración de cambio

Aquí está el código de función:

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
}

los mejores escenarios seleccionan el primer elemento de las listas, los peores casos usan valores no existentes.

aquí están los resultados:

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 gana todo el camino, el peor de los casos es increíblemente más rápido que el mejor de los casos. Los mapas son lo peor y la lista está más cerca de cambiar.

Entonces, la moraleja es: si tiene una lista estática, razonablemente pequeña, la declaración de cambio es el camino a seguir.

Igor
fuente
No sé si Go optimiza este caso, pero ¿hay alguna diferencia si mueve la inicialización de la lista / mapa fuera de la función de prueba?
w00t
Tengo curiosidad por ver cómo se desarrollaría la comparación con una lista ordenada y si el tipo valdría la pena
Michael Draper
¿Qué pasa en :lugar de ,en la declaración de cambio? ¿Lo hace más rápido?
Thomas Sauvajon
Intenté usar varias casedeclaraciones en lugar de un solo caso. Los resultados son sensiblemente iguales con ambas funciones.
Thomas Sauvajon
7

Otra opción es usar un mapa como conjunto. Utiliza solo las teclas y hacer que el valor sea algo así como un booleano que siempre es cierto. Luego puede verificar fácilmente si el mapa contiene la clave o no. Esto es útil si necesita el comportamiento de un conjunto, donde si agrega un valor varias veces es solo en el conjunto una vez.

Aquí hay un ejemplo simple donde agrego números aleatorios como claves a un mapa. Si el mismo número se genera más de una vez, no importa, solo aparecerá en el mapa final una vez. Luego utilizo un simple if check para ver si una clave está en el mapa 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)
        }
    }
}

Aquí está en el patio de recreo

noble
fuente
0

Esto es lo más cerca que puedo llegar a la sensación natural del operador "en" de Python. Tienes que definir tu propio tipo. Luego puede ampliar la funcionalidad de ese tipo agregando un método como "has" que se comporta como cabría esperar.

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

Tengo una biblioteca de utilidades donde defino algunas cosas comunes como esta para varios tipos de sectores, como los que contienen enteros o mis otras estructuras.

Sí, se ejecuta en tiempo lineal, pero ese no es el punto. El punto es preguntar y aprender qué construcciones de lenguaje común Go tiene y no tiene. Es un buen ejercicio. Si esta respuesta es tonta o útil depende del lector.

IcarusNM
fuente