Apakah Go memiliki konstruksi “jika x in” yang mirip dengan Python?

289

Tanpa mengulangi seluruh array, bagaimana saya bisa memeriksa apakah xdalam array menggunakan Go? Apakah bahasa memiliki konstruk?

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


sumber
2
Mungkin merupakan
7
AFAIK, Tidak ada tulisan cepat untuk itu. Secara internal, python juga beralih ke array, tidak ada yang terjadi di sekitar itu.
Danish94
6
BTW, catatan penting untuk ini adalah bahwa tidak ada cara untuk melakukan ini (seperti yang Anda minta) " [w] tanpa iterasi di seluruh array". Membuat loop seperti itu eksplisit (atau di belakang fungsi seperti strings.Index) membantu membuatnya lebih jelas apa yang dilakukan kode. Saya mendapat kesan bahwa mungkin Anda berpikir Python in array:melakukan sesuatu dengan cepat / ajaib. AFAIK tidak. Membuat loop yang eksplisit membantu membuat penulis (dan semua pembaca) sadar dan mempertimbangkan implementasi lain (misalnya peta).
Dave C
5
Namun, jika "x" di set memang sangat cepat.
Roberto Alsina
6
Saya tidak berpikir kami meminta pendekatan cepat yang terprogram, kami hanya meminta yang ringkas (dan masih belum bisa ...)
Migwell

Jawaban:

340

Tidak ada operator bawaan untuk melakukannya di Go. Anda perlu beralih di atas array. Anda dapat menulis fungsi Anda sendiri untuk melakukannya, seperti ini:

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

Jika Anda ingin dapat memeriksa keanggotaan tanpa mengulangi seluruh daftar, Anda perlu menggunakan peta sebagai ganti array atau slice, seperti ini:

visitedURL := map[string]bool {
    "http://www.google.com": true,
    "https://paypal.com": true,
}
if visitedURL[thisSite] {
    fmt.Println("Already been here.")
}
andybalholm
sumber
4
apakah ada cara untuk melakukan ini tanpa menentukan jenis? katakanlah jika saya hanya ingin fungsi needleInHaystack (jarum, tumpukan jerami) umum tanpa metode terpisah untuk setiap jenis tunggal
Allen
25
Itu bisa dilakukan dengan paket mencerminkan, tetapi itu akan cukup tidak efisien (mungkin sekitar lambat seolah-olah Anda menulisnya dalam bahasa yang dinamis seperti Python). Selain itu, tidak. Itulah yang dimaksud orang ketika mereka mengatakan bahwa Go tidak memiliki obat generik.
andybalholm
2
Siapa pun yang menemukan jawaban ini harus memperhatikan bahwa Anda TIDAK BISA mengurutkan peta. Kerugian besar menggunakan peta go.
RisingSun
4
Anda tidak dapat mengurutkan peta (objek) dalam Javascript juga. Ini adalah bug v8 yang menolak mengembalikan nilai dalam urutan yang diurutkan berdasarkan abjad.
kumarharsh
7
peta tidak diurutkan dalam sebagian besar bahasa - itu par untuk kursus untuk struktur data peta (hashmap).
Hejazzman
100

Solusi lain jika daftar berisi nilai-nilai statis.

misalnya: memeriksa nilai yang valid dari daftar nilai yang valid:

func IsValidCategory(category string) bool {
    switch category {
    case
        "auto",
        "news",
        "sport",
        "music":
        return true
    }
    return false
}
sebest
sumber
11
Ya, bagaimana jika "nilai valid" Anda berasal dari basis data?
Rami Dabain
Ya, ini rapi tetapi hanya ketika nilai-nilai ini dapat didefinisikan sebelumnya.
piggybox
2
@RonanDejhero maka saya bisa menggunakan WHERE: myValue IN (subquery) :)
anilech
3
Ini rapi dibandingkan dengan jawaban teratas
piggybox
50

Ini adalah kutipan dari buku "Programming in Go: Membuat Aplikasi untuk Abad 21":

Menggunakan pencarian linear sederhana seperti ini adalah satu-satunya pilihan untuk data yang tidak disortir dan baik untuk irisan kecil (hingga ratusan item). Tetapi untuk irisan yang lebih besar — ​​terutama jika kita melakukan pencarian berulang kali — pencarian linier sangat tidak efisien, rata-rata membutuhkan setengah item untuk dibandingkan setiap kali.

Go menyediakan metode sort.Search () yang menggunakan algoritma pencarian biner: Ini membutuhkan perbandingan hanya item log2 (n) (di mana n adalah jumlah item) setiap kali. Untuk menempatkan ini dalam perspektif, pencarian linear dari 10.000 item membutuhkan rata-rata 500.000 perbandingan, dengan kasus terburuk perbandingan 1000000; pencarian biner membutuhkan paling banyak 20 perbandingan, bahkan dalam kasus terburuk.

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
sumber
10
Ini hanya masuk akal jika Anda melakukan pencarian berulang. Kalau tidak, Anda punya kompleksitas n * log (n) * log (n) untuk pencarian sortir dan biner, dibandingkan hanya n untuk pencarian linear.
christian
1
Ini sebenarnya hanya n*log(n) + log(n), karena ini adalah dua operasi independen yang konsekuen
pomo_mondreganto
27

Contoh di atas menggunakan pengurutan dekat, tetapi dalam kasus string cukup gunakan 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
sumber
2
Jawaban ini terlihat seperti salinan yang kurang informatif dari jawaban di bawah ini, yang terjadi sebelum jawaban ini.
cytinus
@cytinus Apa jawaban yang Anda maksud? Itu satu-satunya yang saya lihat berdasarkan sort.SearchStrings.
akim
1
Saya mendapat speedup 100x pada pencarian berulang melalui sepotong besar.
Xeoncross
20

Hanya punya pertanyaan serupa dan memutuskan untuk mencoba beberapa saran di utas ini.

Saya telah membuat tolok ukur skenario kasus terbaik dan terburuk dari 3 jenis pencarian:

  • menggunakan peta
  • menggunakan daftar
  • menggunakan pernyataan switch

ini kode fungsinya:

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
}

skenario kasus terbaik memilih item pertama dalam daftar, yang terburuk menggunakan nilai tidak ada.

berikut hasilnya:

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

Alihkan kemenangan, case terburuk jauh lebih cepat daripada case terbaik. Peta adalah yang terburuk dan daftar lebih dekat untuk beralih.

Jadi moralnya adalah: Jika Anda memiliki daftar statis, cukup kecil, pergantian pernyataan adalah cara untuk pergi.

Igor
sumber
Saya tidak tahu apakah Go mengoptimalkan kasus ini, tetapi apakah ada bedanya jika Anda memindahkan inisialisasi daftar / peta di luar fungsi tes?
w00t
Saya ingin tahu bagaimana perbandingan akan dimainkan dengan daftar yang disortir dan apakah jenis itu akan sepadan
Michael Draper
Bagaimana dengan :alih - alih ,dalam pernyataan switch? Apakah ini membuatnya lebih cepat?
Thomas Sauvajon
Saya mencoba menggunakan beberapa casepernyataan, bukan satu kasus. Hasilnya masuk akal sama dengan kedua fungsi.
Thomas Sauvajon
7

Pilihan lain adalah menggunakan peta sebagai set. Anda hanya menggunakan kunci dan memiliki nilai menjadi sesuatu seperti boolean yang selalu benar. Kemudian Anda dapat dengan mudah memeriksa apakah peta tersebut berisi kunci atau tidak. Ini berguna jika Anda membutuhkan perilaku set, di mana jika Anda menambahkan nilai beberapa kali, itu hanya di set sekali.

Berikut adalah contoh sederhana di mana saya menambahkan angka acak sebagai kunci ke peta. Jika nomor yang sama dihasilkan lebih dari satu kali tidak masalah, itu hanya akan muncul di peta akhir satu kali. Lalu saya menggunakan cek sederhana jika untuk melihat apakah kunci ada di peta atau tidak.

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

Ini dia tempat bermain

mulia
sumber
0

Ini sedekat yang saya bisa rasakan secara alami oleh operator "in" Python. Anda harus menentukan tipe Anda sendiri. Kemudian Anda dapat memperluas fungsionalitas jenis itu dengan menambahkan metode seperti "memiliki" yang berperilaku seperti yang Anda harapkan.

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

Saya memiliki perpustakaan utilitas di mana saya mendefinisikan beberapa hal umum seperti ini untuk beberapa jenis irisan, seperti yang mengandung bilangan bulat atau struct saya sendiri.

Ya, ini berjalan dalam waktu linier, tapi bukan itu intinya. Intinya adalah untuk bertanya dan mempelajari bahasa umum apa yang Go miliki dan tidak miliki. Ini latihan yang bagus. Apakah jawaban ini konyol atau berguna terserah pembaca.

IcarusNM
sumber