O Go tem uma construção "se x in" semelhante ao Python?

289

Sem iterar sobre toda a matriz, como posso verificar se xna matriz usando Go? A linguagem tem uma construção?

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


fonte
2
Pode ser um joguete de stackoverflow.com/q/8307478/180100
7
AFAIK, Não há taquigrafia para isso. Internamente, o python também itera sobre a matriz, não há como contornar isso.
Danish94
6
BTW, uma observação importante disso é que não há como fazer isso (conforme solicitado) " [sem] a iteração em toda a matriz". Tornar esses loops explícitos (ou por trás de uma função como strings.Index) ajuda a tornar mais óbvio o que o código está fazendo. Tenho a impressão de que talvez você ache que o Python in array:está fazendo algo rápido / mágico. AFAIK não é. Tornar o loop explícito ajuda a conscientizar o escritor (e todos os leitores) e considerar outras implementações (por exemplo, um mapa).
Dave C
5
No entanto, se "x" no conjunto é realmente muito rápido.
Roberto Alsina
6
Eu não acho que nós estamos pedindo uma abordagem de programação rápida, estamos apenas a pedir um conciso (e ainda não obteve ...)
Migwell

Respostas:

340

Não há um operador interno para fazê-lo no Go. Você precisa iterar sobre a matriz. Você pode escrever sua própria função para fazê-lo, assim:

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

Se você quiser verificar a associação sem iterar a lista inteira, precisará usar um mapa em vez de uma matriz ou fatia, assim:

visitedURL := map[string]bool {
    "http://www.google.com": true,
    "https://paypal.com": true,
}
if visitedURL[thisSite] {
    fmt.Println("Already been here.")
}
andybalholm
fonte
4
existe alguma maneira de fazer isso sem especificar o tipo? vamos dizer que se eu quiser apenas uma função geral needleInHaystack (agulha, palheiro), sem métodos distintos para cada tipo
Allen
25
Isso poderia ser feito com o pacote reflect, mas seria bastante ineficiente (provavelmente tão lento quanto se você o escrevesse em uma linguagem dinâmica como o Python). Fora isso, não. É o que as pessoas querem dizer quando dizem que o Go não tem genéricos.
18715 Andybalholm
2
Qualquer pessoa que se deparou com essa resposta deve observar que NÃO PODE classificar mapas. Grande desvantagem no uso de mapas go.
RisingSun #
4
você também não pode classificar mapas (objetos) em Javascript. É um bug da v8 que objetos retornam valores em ordem alfabética.
kumarharsh
7
os mapas não são classificados na maioria dos idiomas - é o par para o curso de uma estrutura de dados de mapas (hashmap).
Hejazzman 29/03/19
100

Outra solução se a lista contiver valores estáticos.

por exemplo: verificação de um valor válido em uma lista de valores válidos:

func IsValidCategory(category string) bool {
    switch category {
    case
        "auto",
        "news",
        "sport",
        "music":
        return true
    }
    return false
}
sebest
fonte
11
Sim, e se seus "valores válidos" vierem de um banco de dados?
Rami Dabain
Sim, isso é legal, mas apenas quando esses valores podem ser definidos com antecedência.
piggybox
2
@RonanDejhero então eu poderia usar ONDE: myValue IN (subconsulta) :)
anilech
3
Isso é legal em comparação com a resposta principal
#
50

Esta é uma citação do livro "Programming in Go: Criando aplicativos para o século XXI":

Usar uma pesquisa linear simples como essa é a única opção para dados não classificados e é bom para pequenas fatias (até centenas de itens). Porém, para fatias maiores - especialmente se estamos realizando pesquisas repetidamente - a pesquisa linear é muito ineficiente, exigindo, em média, metade dos itens a serem comparados a cada vez.

Go fornece um método sort.Search () que usa o algoritmo de pesquisa binária: Isso requer a comparação de apenas itens log2 (n) (onde n é o número de itens) a cada vez. Para colocar isso em perspectiva, uma pesquisa linear de 1000000 itens requer 500000 comparações em média, com o pior caso de 1000000 comparações; uma pesquisa binária precisa de no máximo 20 comparações, mesmo no pior caso.

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
fonte
10
Isso só faz sentido se você fizer pesquisas repetidas. Caso contrário, você terá complexidade n * log (n) * log (n) para a classificação e a pesquisa binária, contra apenas n na pesquisa linear.
19417 christian
1
Na verdade n*log(n) + log(n), é justo , pois são duas operações independentes consequentes
pomo_mondreganto
27

O exemplo acima, usando sort, está próximo, mas no caso de strings, basta usar 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
fonte
2
Esta resposta parece uma pasta de cópia menos informativa da resposta abaixo, que ocorreu antes desta resposta.
Cytinus
@cytinus A que resposta você está se referindo? Esse é o único que eu vejo baseado sort.SearchStrings.
akim
1
Consegui uma aceleração de 100x em pesquisas repetidas em uma fatia grande.
Xeoncross
20

Só tinha uma pergunta semelhante e decidiu tentar algumas das sugestões neste tópico.

Comparei os melhores e os piores cenários de três tipos de pesquisa:

  • usando um mapa
  • usando uma lista
  • usando uma instrução switch

aqui está o código da função:

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
}

os cenários de melhor caso escolhem o primeiro item nas listas; os piores casos usam valor inexistente.

aqui estão os 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

O switch vence todo o caminho, o pior caso é surpreendentemente mais rápido que o melhor. Os mapas são os piores e a lista está mais perto de mudar.

Portanto, a moral é: se você tem uma lista estática e razoavelmente pequena, a instrução switch é o caminho a seguir.

Igor
fonte
Não sei se o Go otimiza esse caso, mas faz diferença se você mover a inicialização da lista / mapa para fora da função de teste?
w00t
Estou curioso para ver como a comparação iria jogar fora com uma lista ordenada e se o tipo valeria a pena
Michael Draper
Que tal em :vez de ,na instrução switch? Isso torna mais rápido?
Thomas Sauvajon 10/02
Tentei usar várias caseinstruções em vez de um único caso. Os resultados são sensivelmente os mesmos com ambas as funções.
Thomas Sauvajon
7

Outra opção é usar um mapa como um conjunto. Você usa apenas as chaves e o valor é algo como um booleano que é sempre verdade. Depois, você pode facilmente verificar se o mapa contém a chave ou não. Isso é útil se você precisar do comportamento de um conjunto, onde se você adicionar um valor várias vezes, ele será incluído apenas uma vez.

Aqui está um exemplo simples em que adiciono números aleatórios como chaves em um mapa. Se o mesmo número for gerado mais de uma vez, não importa, ele aparecerá no mapa final apenas uma vez. Então eu uso um simples se verificar para ver se uma chave está no mapa ou não.

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

Aqui está em movimento

nobre
fonte
0

Isso é o mais próximo que consigo da sensação natural do operador "in" do Python. Você precisa definir seu próprio tipo. Em seguida, você pode estender a funcionalidade desse tipo adicionando um método como "has" que se comporta como você esperaria.

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

Eu tenho uma biblioteca de utilitários onde defino algumas coisas comuns como essa para vários tipos de fatias, como aquelas que contêm números inteiros ou minhas próprias estruturas.

Sim, é executado em tempo linear, mas esse não é o ponto. O objetivo é perguntar e aprender quais construções de linguagem comum o Go tem e não tem. É um bom exercício. Se essa resposta é tola ou útil, cabe ao leitor.

IcarusNM
fonte