Имеет ли Go конструкцию "if x in", похожую на Python?

Без итерации по всему массиву, как я могу проверить, если x в массиве с помощью Go? У языка есть конструкция?

Как и Python: if "x" in array:...

Ответ 1

В Go нет встроенного оператора. Вам нужно перебирать массив. Вы можете написать свою собственную функцию, чтобы сделать это, например:

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

Если вы хотите проверить членство без итерации по всему списку, вам нужно использовать карту вместо массива или среза, например:

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

Ответ 2

Другое решение, если список содержит статические значения.

например: проверка допустимого значения из списка допустимых значений:

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

Ответ 3

Это цитата из книги "Программирование в Go: создание приложений для 21-го века":

Использование простого линейного поиска, подобного этому, является единственным вариантом для несортированных данных и отлично подходит для небольших фрагментов (до сотен предметов). Но для более крупные ломтики, особенно если мы выполняем поиск повторно - линейный поиск очень неэффективен, в среднем требуется половина предметов для сравнения каждый раз.

Go предоставляет метод sort.Search(), который использует двоичный поиск алгоритм: для этого требуется сравнение только элементов log2 (n) (где n количество элементов) каждый раз. Чтобы представить это в перспективе, линейный поиск 1000000 предметов требует в среднем 500000 сравнений, с худшим случаем 1000000 сравнений; бинарный поиск требует наиболее 20 сравнений, даже в худшем случае.

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

Ответ 4

Приведенный выше пример с использованием сортировки близок, но в случае строк просто используйте 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

Ответ 5

Просто у меня был похожий вопрос, и он решил попробовать некоторые из предложений в этой теме.

Я сравнил лучшие и худшие сценарии 3 типов поиска:

  • используя карту
  • используя список
  • используя оператор switch

здесь код функции:

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
}

в лучших случаях выбирают первый элемент в списках, в худших - несуществующее значение.

вот результаты:

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

Переключатель выигрывает до конца, в худшем случае это намного быстрее, чем в лучшем случае. Карты являются худшими, и список ближе к переключению.

Итак, мораль такова: если у вас есть статичный, достаточно небольшой список, выражение о переключении - путь.

Ответ 6

Другой вариант - использовать карту как набор. Вы используете только ключи, и значение имеет что-то вроде логического значения, которое всегда истинно. Тогда вы можете легко проверить, содержит ли карта ключ или нет. Это полезно, если вам нужно поведение набора, где если вы добавляете значение несколько раз, оно только в наборе один раз.

Вот простой пример, где я добавляю случайные числа в качестве ключей к карте. Если одно и то же число генерируется более одного раза, это не имеет значения, оно появится на итоговой карте только один раз. Затем я использую простую проверку, чтобы увидеть, есть ли ключ на карте или нет.

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

Вот она на ходу детская площадка