如何检查x是否在一个数组中,而不遍历整个数组,使用Go?语言中有这样的结构吗?
如在Python中:
if "x" in array:
# do something
如何检查x是否在一个数组中,而不遍历整个数组,使用Go?语言中有这样的结构吗?
如在Python中:
if "x" in array:
# do something
当前回答
这是我所能得到的最接近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中没有内置的操作符来做这件事。你需要遍历数组。你可以编写自己的函数来实现,就像这样:
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)。
如果你想在不遍历整个列表的情况下检查成员关系,你需要使用map而不是数组或slice,如下所示:
visitedURL := map[string]bool {
"http://www.google.com": true,
"https://paypal.com": true,
}
if visitedURL[thisSite] {
fmt.Println("Already been here.")
}
我也有一个类似的问题,并决定尝试一下这篇文章中的一些建议。
我已经对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语句是最好的方法。
另一种选择是使用地图作为集合。你只使用键,并让值为布尔值,总是true。然后,您可以轻松地检查映射是否包含键。如果你需要一个集合的行为,这很有用,如果你多次添加一个值,它只在集合中出现一次。
这是一个简单的例子,我添加随机数作为键映射。如果相同的数字生成了不止一次,那也没关系,它只会在最终地图中出现一次。然后我使用一个简单的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)
}
}
}
这是在围棋场上
这是我所能得到的最接近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拥有和没有的通用语言结构。这是一个很好的练习。这个答案是傻还是有用取决于读者。
上面使用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