配列全体を反復しないで、Goを使用して配列内のx
を確認するにはどうすればよいですか。言語には構成要素がありますか?
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.")
}
リストに静的な値が含まれている場合の別の解決策。
例:有効値のリストから有効値をチェックする:
func IsValidCategory(category string) bool {
switch category {
case
"auto",
"news",
"sport",
"music":
return true
}
return false
}
これは本「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)
}
上記の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)
}
ちょうど同じような質問があり、このスレッドでいくつかの提案を試してみることにしました。
私は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ステートメントを使用します。
もう1つの選択肢は、マップをセットとして使用することです。あなただけのキーを使用し、値が常に真実であるブール値のようなものであることを持っています。その後、マップにキーが含まれているかどうかを簡単に確認できます。これは、セットの動作が必要な場合に便利です。値を複数回追加しても、それはセット内で1回だけです。
これは、乱数をキーとしてマップに追加する簡単な例です。同じ数が2回以上生成されても問題ありませんが、最終的なマップには1回しか表示されません。それから、キーがマップ内にあるかどうかを確認するために単純な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)
}
}
}