Go是否具有类似于Python的“ if x in”构造?


289

如果不遍历整个数组,如何x使用Go 检查数组中是否存在?语言有结构吗?

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



7
AFAIK,目前尚无速记。在内部,python还会在数组上进行迭代,因此无法解决。
Danish94 2013年

6
顺便说一句,这一个重要的注意的是,有没有办法做到这一点(因为你问的)“ [W] ithout迭代整个阵列之上”。使这样的循环显式(或在诸如之类的函数之后strings.Index)有助于使代码的作用更加明显。我的印象是,也许您认为Python in array:正在快速/神奇地完成任务。并非如此。明确显示循环有助于使作者(和所有读者)意识到并考虑其他实现(例如,地图)。
戴夫C

5
但是,如果集合中的“ x”确实非常快。
罗伯托·阿尔西纳

6
我不认为我们正在寻求一种程序上快速的方法,我们只是在寻求一种简洁的方法(但仍然没有...)
Migwell,

Answers:


340

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

4
没有指定类型有什么办法做到这一点吗?假设我只想使用普通的needleInHaystack(needle,haystack)函数,而无需为每种类型使用单独的方法
艾伦(Allen

25
可以使用反射包来完成,但是效率很低(可能就像用Python这样的动态语言写出来一样慢)。除此之外,没有。这就是人们说Go没有泛型时的意思。
andybalholm 2015年

2
偶然发现此答案的任何人都必须注意,您无法对地图进行排序。使用go地图的不利之处很大。
RisingSun

4
您也无法使用Javascript对地图(对象)进行排序。这是一个v8 错误,对象按字母顺序返回值。
kumarharsh

7
地图不是使用大多数语言来排序的-这与地图数据结构(hashmap)的课程相当。
Hejazzman

100

列表包含静态值的另一种解决方案。

例如:从有效值列表中检查有效值:

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

11
是的,如果您的“有效值”来自数据库怎么办?
拉米·达巴因

是的,这很简单,但前提是可以预先定义这些值。
piggybox

2
@RonanDejhero然后我可以使用WHERE:myValue IN(子查询):)
anilech

3
与最高答案相比,这很干净
储钱箱'19

50

这是从“ 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


10
仅当您重复搜索时才有意义。否则,排序和二进制搜索的复杂度为n * log(n)* log(n),而线性搜索的复杂度仅为n。
基督教徒

1
它实际上只是n*log(n) + log(n),因为它是两个相继独立运营
pomo_mondreganto

27

上面使用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


2
该答案看起来像是下面答案的信息量较少的副本,粘贴在此答案之前。
cytinus

@cytinus您指的是什么答案?这是我基于看到的唯一一个sort.SearchStrings
akim

1
重复搜索大片时,我的速度提高了100倍。
Xeoncross '18年

20

只是有类似的问题,并决定尝试该线程中的一些建议。

我已经对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

Switch一路取胜,最坏的情况比最理想的情况快得多。地图最差,列表更接近切换。

因此,其道理是:如果您有一个静态的,相当小的列表,则使用switch语句是可行的方法。


我不知道Go是否可以优化这种情况,但是如果您将列表/映射的初始化移到测试函数之外,会有所不同吗?
w00t

我很好奇看到比较如何与排序列表一起进行以及排序是否值得
Michael Draper

怎么样:,而不是,在switch语句?它使速度更快吗?
Thomas Sauvajon

我尝试使用多个case语句而不是单个案例。这两个函数的结果明显相同。
Thomas Sauvajon

7

另一种选择是使用地图作为集合。您只需要使用键,并将值设为类似布尔值的值就总是正确。然后,您可以轻松检查地图是否包含密钥。如果需要集合的行为,这很有用,如果您多次添加一个值,则该值仅在集合中一次。

这是一个简单的示例,其中我将随机数添加为地图的键。如果多次生成相同的数字无关紧要,则它只会在最终地图中出现一次。然后,我使用一个简单的if检查来查看键是否在地图中。

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

这是在旅途中的操场


0

这与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具有和不具有的通用语言构造。这是一个很好的练习。这个答案是愚蠢的还是有用的取决于读者。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.