Go语言有类似Python的“if x in”结构吗?

k97glaaz  于 2023-02-17  发布在  Go
关注(0)|答案(9)|浏览(149)

我怎样才能检查x是否在一个数组中而不用遍历整个数组呢?Go语言有这样的构造吗?
就像Python:

if "x" in array: 
  # do something
9udxz4iz

9udxz4iz1#

Go语言中没有内置的操作符来完成这一操作,你需要遍历数组,你可以编写自己的函数来完成,如下所示:

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

或者在Go语言1.18或更高版本中,可以使用slices.Contains(golang.org/x/exp/slices)。
如果你想在不遍历整个列表的情况下检查成员关系,你需要使用map而不是数组或切片,如下所示:

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

cvxl0en22#

如果列表包含静态值,则另一种解决方案。
例如:从有效值列表中检查有效值:

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

mnowg1ta3#

这句话摘自《Go语言中的编程:为21世纪创建应用程序”:
对于未排序的数据,使用这样一个简单的线性搜索是唯一的选择,对于小的切片(多达数百个条目)来说是不错的,但是对于较大的切片--特别是当我们重复执行搜索时--线性搜索效率非常低,平均来说每次需要比较一半的条目。
Go语言提供了一个sort.search()方法,该方法使用了二进制搜索算法:这需要每次仅比较log 2(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

qeeaahzv

qeeaahzv4#

只是有一个类似的问题,并决定尝试一些建议,在这个线程。
我已经对3种类型的查找的最佳和最差情况进行了基准测试:

  • 使用Map
  • 使用列表
  • 使用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

交换机一路获胜,最坏的情况比最好的情况快得多。
Map是最糟糕的,列表更接近开关。
所以寓意是:如果您有一个静态的、相当小的列表,那么switch语句是最好的选择。

s8vozzvw

s8vozzvw5#

上面使用sort的例子很接近,但是在字符串的情况下,只需使用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

ghg1uchk

ghg1uchk6#

这是我所能得到的最接近Python“in”操作符的自然感觉。你必须定义自己的类型。然后你可以通过添加一个像“has”这样的方法来扩展该类型的功能,它的行为就像你所希望的那样。

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

我有一个实用程序库,在那里我为几种类型的切片定义了一些常见的东西,比如那些包含整数或我自己的其他结构体的切片。
是的,它是在线性时间内运行的,但这不是重点,重点是问并学习Go语言有哪些共同的语言结构,哪些没有,这是一个很好的练习,这个答案是愚蠢的还是有用的,取决于读者。

gcuhipw9

gcuhipw97#

另一种选择是使用map作为集合。您只使用键,并让值为布尔值,该值始终为真。然后您可以轻松地检查map是否包含键。如果您需要集合的行为,这将非常有用。在这种情况下,如果您多次添加一个值,则该值在集合中只存在一次。
下面是一个简单的例子,我将随机数作为键添加到Map中。如果同一个数字被生成多次,也没关系,它只会在最终的Map中出现一次。然后我使用一个简单的if检查来查看键是否在Map中。

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

Here it is on the go playground

2w3kk1z5

2w3kk1z58#

在Go语言1.18+中,现在可以声明泛型Contains函数,它也在实验切片函数中实现,适用于任何类似的类型

func Contains[T comparable](arr []T, x T) bool {
    for _, v := range arr {
        if v == x {
            return true
        }
    }
    return false
}

像这样使用它:

if Contains(arr, "x") {
    // do something
}
// or
if slices.Contains(arr, "x") {
    // do something
}

which I found here

mjqavswn

mjqavswn9#

尝试lo:https://github.com/samber/lo#contains

present := lo.Contains[int]([]int{0, 1, 2, 3, 4, 5}, 5)

相关问题