提问人: 提问时间:3/10/2013 最后编辑:Amal Thundiyil 更新时间:8/13/2023 访问量:432612
Go 是否有类似于 Python 的“if x in”结构?[复制]
Does Go have "if x in" construct similar to Python? [duplicate]
问:
如何使用 Go 在不遍历整个数组的情况下检查数组中是否在数组中?语言有这个结构吗?x
就像在 Python 中一样:
if "x" in array:
# do something
答:
在 Go 中没有内置运算符来执行此操作。您需要遍历数组。你可以编写自己的函数来做到这一点,如下所示:
func stringInSlice(a string, list []string) bool {
for _, b := range list {
if b == a {
return true
}
}
return false
}
或者在 Go 1.18 或更高版本中,您可以使用切片。包含
(来自 golang.org/x/exp/slices)。
如果您希望能够在不遍历整个列表的情况下检查成员资格,则需要使用映射而不是数组或切片,如下所示:
visitedURL := map[string]bool {
"http://www.google.com": true,
"https://paypal.com": true,
}
if visitedURL[thisSite] {
fmt.Println("Already been here.")
}
评论
这是《Programming in Go: Creating Applications for the 21st Century》一书中的一段话:
使用像这样的简单线性搜索是未排序的唯一选择 数据,适用于小切片(最多数百个项目)。但是对于 较大的切片(尤其是当我们重复执行搜索时), 线性搜索效率非常低,平均需要一半的项目 每次进行比较。
Go 提供了一种排序。Search() 方法,使用二进制搜索 algorithm:这只需要比较 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
评论
n*log(n) + log(n)
如果列表包含静态值,则另一种解决方案。
例如:从有效值列表中检查有效值:
func IsValidCategory(category string) bool {
switch category {
case
"auto",
"news",
"sport",
"music":
return true
}
return false
}
评论
上面使用 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
评论
sort.SearchStrings
另一种选择是将地图用作集合。您只使用键,并将值设置为始终为真的布尔值。然后,您可以轻松检查地图是否包含密钥。如果您需要集合的行为,这将很有用,如果多次添加一个值,则它只会在集合中出现一次。
这是一个简单的示例,我将随机数作为键添加到地图中。如果多次生成相同的数字,则没关系,它只会在最终地图中出现一次。然后,我使用一个简单的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)
}
}
}
刚刚有一个类似的问题,并决定尝试此线程中的一些建议。
我已经对 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 语句是要走的路。
评论
:
,
func Bench...
这尽可能接近 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 有哪些通用语言结构,以及没有哪些通用语言结构。这是一个很好的练习。这个答案是愚蠢的还是有用的取决于读者。
在 Go 1.18+1.21+ 中,现在可以声明通用函数或将内置的 slice 包导入到实验性切片包中。它适用于任何可比类型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
}
或通过导入
// func[S ~[]E, E comparable](s S, v E) bool
if slices.Contains(arr, "x") {
// do something
}
尝试 : https://github.com/samber/lo#containslo
present := lo.Contains[int]([]int{0, 1, 2, 3, 4, 5}, 5)
评论
strings.Index
in array: