配列全体を反復処理せずx
に、 Goを使用して配列内にあるかどうかを確認するにはどうすればよいですか?言語には構成がありますか?
Pythonのように:if "x" in array: ...
配列全体を反復処理せずx
に、 Goを使用して配列内にあるかどうかを確認するにはどうすればよいですか?言語には構成がありますか?
Pythonのように:if "x" in array: ...
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.")
}
これは、「Programming in Go: Making Applications for the 21st Century」という本からの引用です。
このような単純な線形検索を使用することは、並べ替えられていないデータに対する唯一のオプションであり、小さなスライス (数百項目まで) には問題ありません。しかし、より大きなスライスの場合、特に検索を繰り返し実行している場合、線形検索は非常に非効率的であり、平均して毎回半分の項目を比較する必要があります。
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)
}
同様の質問があり、このスレッドのいくつかの提案を試すことにしました。
3 種類のルックアップの最良のシナリオと最悪のシナリオのベンチマークを行いました。
関数コードは次のとおりです。
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 ステートメントを使用することをお勧めします。