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

huangapple go评论81阅读模式
英文:

Does Go have "if x in" construct similar to Python?

问题

如何在不遍历整个数组的情况下,使用Go语言检查x是否在数组中?这种语言是否有相应的构造?

就像在Python中:

if "x" in array:
  # 做一些事情
英文:

How can I check if x is in an array without iterating over the entire array, using Go? Does the language have a construct for this?

Like in Python:

if "x" in array: 
  # do something

答案1

得分: 537

在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)。

如果你想要能够在不遍历整个列表的情况下检查成员资格,你需要使用映射而不是数组或切片,就像这样:

visitedURL := map[string]bool {
    "http://www.google.com": true,
    "https://paypal.com": true,
}
if visitedURL[thisSite] {
    fmt.Println("已经访问过这里了。")
}
英文:

There is no built-in operator to do it in Go. You need to iterate over the array. You can write your own function to do it, like this:

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

Or in Go 1.18 or newer, you can use slices.Contains (from golang.org/x/exp/slices).

If you want to be able to check for membership without iterating over the whole list, you need to use a map instead of an array or slice, like this:

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

答案2

得分: 160

另一种解决方案是,如果列表包含静态值。

例如:检查有效值是否来自有效值列表:

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

Another solution if the list contains static values.

eg: checking for a valid value from a list of valid values:

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

答案3

得分: 57

这是来自书籍《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("在files[%d]找到了\"%s\"\n", i, files[i])
}
英文:

This is quote from the book "Programming in Go: Creating Applications for the 21st Century":

> Using a simple linear search like this is the only option for unsorted
> data and is fine for small slices (up to hundreds of items). But for
> larger slices—especially if we are performing searches repeatedly—the
> linear search is very inefficient, on average requiring half the items
> to be compared each time.
>
> Go provides a sort.Search() method which uses the binary search
> algorithm: This requires the comparison of only log2(n) items (where n
> is the number of items) each time. To put this in perspective, a
> linear search of 1000000 items requires 500000 comparisons on average,
> with a worst case of 1000000 comparisons; a binary search needs at
> most 20 comparisons, even in the worst case.

files := []string{&quot;Test.conf&quot;, &quot;util.go&quot;, &quot;Makefile&quot;, &quot;misc.go&quot;, &quot;main.go&quot;}
target := &quot;Makefile&quot;
sort.Strings(files)
i := sort.Search(len(files),
	func(i int) bool { return files[i] &gt;= target })
if i &lt; len(files) &amp;&amp; files[i] == target {
	fmt.Printf(&quot;found \&quot;%s\&quot; at files[%d]\n&quot;, files[i], i)
}

https://play.golang.org/p/UIndYQ8FeW

答案4

得分: 45

我刚刚有一个类似的问题,并决定尝试一些这个主题中的建议。

我对3种查找方式的最佳和最坏情况进行了基准测试:

  • 使用map
  • 使用list
  • 使用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在所有情况下都胜出,最坏情况比最佳情况快得多。

Map是最差的,而list接近于switch。

所以结论是:
如果你有一个静态的、相对较小的列表,使用switch语句是最好的方法。

英文:

Just had a similar question and decided to try out some of the suggestions in this thread.

I've benchmarked best and worst-case scenarios of 3 types of lookup:

  • using a map
  • using a list
  • using a switch statement

Here's the function code:

func belongsToMap(lookup string) bool {
list := map[string]bool{
    &quot;900898296857&quot;: true,
    &quot;900898302052&quot;: true,
    &quot;900898296492&quot;: true,
    &quot;900898296850&quot;: true,
    &quot;900898296703&quot;: true,
    &quot;900898296633&quot;: true,
    &quot;900898296613&quot;: true,
    &quot;900898296615&quot;: true,
    &quot;900898296620&quot;: true,
    &quot;900898296636&quot;: true,
}
if _, ok := list[lookup]; ok {
    return true
} else {
    return false
}
}


func belongsToList(lookup string) bool {
list := []string{
    &quot;900898296857&quot;,
    &quot;900898302052&quot;,
    &quot;900898296492&quot;,
    &quot;900898296850&quot;,
    &quot;900898296703&quot;,
    &quot;900898296633&quot;,
    &quot;900898296613&quot;,
    &quot;900898296615&quot;,
    &quot;900898296620&quot;,
    &quot;900898296636&quot;,
}
for _, val := range list {
    if val == lookup {
        return true
    }
}
return false
}

func belongsToSwitch(lookup string) bool {
switch lookup {
case
    &quot;900898296857&quot;,
    &quot;900898302052&quot;,
    &quot;900898296492&quot;,
    &quot;900898296850&quot;,
    &quot;900898296703&quot;,
    &quot;900898296633&quot;,
    &quot;900898296613&quot;,
    &quot;900898296615&quot;,
    &quot;900898296620&quot;,
    &quot;900898296636&quot;:
    return true
}
return false
}

Best-case scenarios pick the first item in lists, worst-case ones use nonexistent value.

Here are the results:

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 wins all the way, worst case is surpassingly quicker than best case.

Maps are the worst and list is closer to switch.

So the moral is:
If you have a static, reasonably small list, switch statement is the way to go.

答案5

得分: 29

上面的例子使用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("在files[%d]找到了\"%s\"\n", i, files[i])
}

https://golang.org/pkg/sort/#SearchStrings

英文:

The above example using sort is close, but in the case of strings simply use SearchString:

files := []string{&quot;Test.conf&quot;, &quot;util.go&quot;, &quot;Makefile&quot;, &quot;misc.go&quot;, &quot;main.go&quot;}
target := &quot;Makefile&quot;
sort.Strings(files)
i := sort.SearchStrings(files, target)
if i &lt; len(files) &amp;&amp; files[i] == target {
fmt.Printf(&quot;found \&quot;%s\&quot; at files[%d]\n&quot;, files[i], i)
}

https://golang.org/pkg/sort/#SearchStrings

答案6

得分: 13

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

我有一个实用程序库,其中我为几种类型的切片定义了一些常见的东西,比如包含整数或我自己的其他结构的切片。

是的,它以线性时间运行,但这不是重点。重点是询问并了解Go语言具有和不具有的常见语言结构。这是一个很好的练习。这个答案是愚蠢还是有用,取决于读者。

英文:

This is as close as I can get to the natural feel of Python's "in" operator. You have to define your own type. Then you can extend the functionality of that type by adding a method like "has" which behaves like you'd hope.

package main
import &quot;fmt&quot;
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{&quot;The&quot;, &quot;big&quot;, &quot;dog&quot;, &quot;has&quot;, &quot;fleas&quot;}
if testList.Has(&quot;dog&quot;) {
fmt.Println(&quot;Yay!&quot;)
}
}

I have a utility library where I define a few common things like this for several types of slices, like those containing integers or my own other structs.

Yes, it runs in linear time, but that's not the point. The point is to ask and learn what common language constructs Go has and doesn't have. It's a good exercise. Whether this answer is silly or useful is up to the reader.

答案7

得分: 8

另一个选项是使用map作为集合。你只使用键,将值设为一个类似布尔值的东西,总是为true。然后你可以轻松地检查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)
		}
	}
}

在Go Playground上查看代码

英文:

Another option is using a map as a set. You use just the keys and having the value be something like a boolean that's always true. Then you can easily check if the map contains the key or not. This is useful if you need the behavior of a set, where if you add a value multiple times it's only in the set once.

Here's a simple example where I add random numbers as keys to a map. If the same number is generated more than once it doesn't matter, it will only appear in the final map once. Then I use a simple if check to see if a key is in the map or not.

package main
import (
&quot;fmt&quot;
&quot;math/rand&quot;
)
func main() {
var MAX int = 10
m := make(map[int]bool)
for i := 0; i &lt;= MAX; i++ {
m[rand.Intn(MAX)] = true
}
for i := 0; i &lt;= MAX; i++ {
if _, ok := m[i]; ok {
fmt.Printf(&quot;%v is in map\n&quot;, i)
} else {
fmt.Printf(&quot;%v is not in map\n&quot;, i)
}
}
}

Here it is on the go playground

答案8

得分: 7

在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") {
    // 做一些事情
}
// 或者
if slices.Contains(arr, "x") {
    // 做一些事情
}

我在这里找到的

英文:

In Go 1.18+, you can now declare generic Contains function which is also implemented in the experimental slice function. It works for any comparable type

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

and use it like this:

if Contains(arr, &quot;x&quot;) {
    // do something
}
// or
if slices.Contains(arr, &quot;x&quot;) {
    // do something
}

which I found here

答案9

得分: 1

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

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

try lo: https://github.com/samber/lo#contains

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

huangapple
  • 本文由 发表于 2013年3月10日 23:15:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/15323767.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定