Go'nun Python'a benzer bir “if x in” yapısı var mı?


290

Olmadan tüm dizi boyunca yineleme, nasıl kontrol edebilirsiniz xGit kullanarak dizide? Dilin bir yapısı var mı?

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



7
AFAIK, Bunun için kestirme yol yok. Dahili olarak, python da dizi üzerinde tekrarlar, bunun etrafında bir gidiş yoktur.
Danimarka94

6
BTW, bunun için önemli bir not, bunu yapmak için bir yolun olmamasıdır (istediğiniz gibi) " [w] tüm dizi üzerinde yineleme yapmadan ". Bu tür döngüleri açık yapmak (veya gibi bir işlevin arkasında strings.Index) kodun ne yaptığını daha açık hale getirmeye yardımcı olur. Belki de Python'un in array:hızlı / büyülü bir şey yaptığını düşünüyorsunuz . AFAIK öyle değil. Döngüyü açık yapmak, yazarın (ve tüm okuyucuların) farkında olmasına ve diğer uygulamaları (örneğin bir harita) dikkate almaya yardımcı olur.
Dave C

5
Ancak, setteki "x" gerçekten çok hızlıysa.
Roberto Alsina

6
Programsal olarak hızlı bir yaklaşım istediğimizi sanmıyorum, sadece kısa ve öz bir yaklaşım istiyoruz (ve hâlâ da yok ...)
Migwell

Yanıtlar:


340

Go'da yapacak yerleşik bir operatör yoktur. Dizi üzerinde yineleme yapmanız gerekir. Bunu yapmak için kendi işlevinizi yazabilirsiniz, şöyle:

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

Tüm listeyi yinelemeden üyeliği kontrol etmek istiyorsanız, aşağıdaki gibi bir dizi veya dilim yerine bir harita kullanmanız gerekir:

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

4
bunu tür belirtmeden yapmanın bir yolu var mı? her genel tip için ayrı yöntemler olmadan sadece genel bir needleInHaystack (iğne, samanlık) işlevi istersem diyelim
Allen

26
Yansıtma paketi ile yapılabilir, ancak oldukça verimsiz olurdu (muhtemelen Python gibi dinamik bir dilde yazdığınız kadar yavaş). Bunun dışında hayır. Go'nun jenerik olmadığını söyleyen insanlar bunu kastediyor.
andybalholm

2
Bu cevabı tökezleyen herkes haritaları sıralayamayacağınızı bilmelidir. Git haritalar kullanmak için büyük dezavantajı.
RisingSun

4
Javascript'te haritaları (nesneleri) sıralayamazsınız. Nesnelerin değerleri alfabetik olarak sıralı olarak döndürmesi bir v8 hatasıdır .
kumarharsh

7
haritalar çoğu dilde sıralanmamıştır - bu, bir harita veri yapısı (hashmap) için kursa eşittir.
Hejazzman

101

Liste statik değerler içeriyorsa başka bir çözüm.

örneğin: geçerli değerler listesinden geçerli bir değerin kontrol edilmesi:

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

12
Evet, ya "geçerli değerleriniz" bir veritabanından geliyorsa?
Rami Dabain

Evet, bu temiz ama sadece bu değerler önceden tanımlanabiliyorsa.
piggybox

3
@RonanDejhero sonra WHERE kullanabilirsiniz: myValue IN (alt sorgu) :)
anilech

3
Bu en iyi cevaba kıyasla düzgün
piggybox

50

"Go to Programming: 21. Yüzyıl İçin Uygulama Yaratmak" kitabından alıntı:

Bu şekilde basit bir doğrusal arama kullanmak, sıralanmamış veriler için tek seçenektir ve küçük dilimler için (yüzlerce öğeye kadar) uygundur. Ancak daha büyük dilimler için - özellikle de tekrar tekrar arama yapıyorsak - doğrusal arama çok verimsizdir, ortalama olarak her seferinde öğelerin yarısının karşılaştırılmasını gerektirir.

Go, ikili arama algoritmasını kullanan bir sort.Search () yöntemi sağlar: Bu, her seferinde yalnızca log2 (n) öğelerinin (burada n öğe sayısıdır) karşılaştırılmasını gerektirir. Bunu perspektife sokmak için, 1000000 öğenin doğrusal bir araştırması, en kötü 1000000 karşılaştırması ile ortalama 500000 karşılaştırmayı gerektirir; bir ikili arama en kötü durumda bile en fazla 20 karşılaştırmaya ihtiyaç duyar.

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
Bu sadece tekrarlanan aramalar yaparsanız mantıklıdır. Aksi takdirde, sıralama ve ikili arama için n * log (n) * log (n) karmaşıklığına sahipsiniz, lineer arama için sadece n yerine.
christian

1
Aslında sadece n*log(n) + log(n)iki ardışık bağımsız operasyon olduğu için
pomo_mondreganto

27

Sıralama kullanan yukarıdaki örnek yakındır, ancak dizeler için SearchString kullanın:

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
Bu cevap, aşağıdaki cevaptan daha az bilgilendirici bir kopya pastası gibi görünüyor.
cytinus

@cytinus Hangi cevaba atıfta bulunuyorsunuz? Buna dayanarak gördüğüm tek şey bu sort.SearchStrings.
akim

1
Büyük bir dilim üzerinden tekrarlanan aramalarda 100 kat hız aldım.
Xeoncross

20

Sadece benzer bir sorum vardı ve bu konudaki bazı önerileri denemeye karar verdim.

3 tür arama için en iyi ve en kötü senaryoları karşılaştırdım:

  • bir harita kullanmak
  • bir liste kullanmak
  • switch deyimi kullanma

İşte fonksiyon kodu:

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
}

en iyi durum senaryoları listelerdeki ilk öğeyi seçer, en kötü durum olanlar var olmayan değeri kullanır.

sonuçlar burada:

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

Anahtar tamamen kazanır, en kötü durum en iyi durumdan çok daha hızlıdır. Haritalar en kötüsüdür ve liste geçişe daha yakındır.

Ahlaki şudur: Statik, oldukça küçük bir listeniz varsa, switch deyimi gitmenin yoludur.


Go'nun bu durumu optimize edip etmediğini bilmiyorum, ancak listenin / haritanın başlatılmasını test işlevinin dışına çıkarırsanız bir fark yaratıyor mu?
w00t

Karşılaştırmanın sıralı bir listeyle nasıl oynayacağını ve sıralamanın buna değip değmeyeceğini merak ediyorum
Michael Draper

Switch ifadesi :yerine ne olacak ,? Daha hızlı mı yapıyor?
Thomas Sauvajon

caseTek bir durum yerine birden çok ifade kullanmayı denedim . Sonuçlar her iki fonksiyonda da mantıklıdır.
Thomas Sauvajon

7

Başka bir seçenek de haritayı küme olarak kullanmaktır. Sadece anahtarları kullanırsınız ve değerin her zaman doğru olan bir boolean gibi olması gerekir. Ardından haritanın anahtarı içerip içermediğini kolayca kontrol edebilirsiniz. Bu, bir kümenin davranışına ihtiyacınız varsa kullanışlıdır; burada bir değeri birden çok kez eklerseniz, bu değer yalnızca kümede bir kez olur.

İşte bir haritaya anahtar olarak rastgele sayılar eklediğim basit bir örnek. Aynı sayı farketmeden bir kereden fazla üretilirse, son haritada yalnızca bir kez görünür. Sonra bir anahtarın harita üzerinde olup olmadığını görmek için basit bir kontrol kullanıyorum.

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

İşte hareket halindeyken oyun alanı


0

Bu, Python'un "inç" operatörünün doğal hissine ulaşabileceğim kadar yakın. Kendi tipinizi tanımlamanız gerekir. Daha sonra "has" gibi bir yöntem ekleyerek umduğunuz gibi davranarak bu türün işlevselliğini genişletebilirsiniz.

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

Tamsayı veya kendi diğer yapıları içerenler gibi çeşitli dilim türleri için böyle birkaç ortak şey tanımladığım bir yarar kitaplığı var.

Evet, lineer zamanda çalışıyor, ama mesele bu değil. Mesele, Go'nun hangi ortak dil yapılarına sahip olup olmadığını sormak ve öğrenmektir. İyi bir egzersiz. Bu cevabın aptalca veya yararlı olup olmadığı okuyucuya bağlıdır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.