GoにはPythonと同様の「if x in」構成がありますか?


289

配列全体を反復処理せずにxGoを使用して配列内にあるかどうかを確認するにはどうすればよいですか?言語には構文がありますか?

Pythonのように: if "x" in array: ...



7
私の知る限りでは、そのための省略形はありません。内部的には、pythonも配列を反復処理します。これを回避する方法はありません。
Danish94 2013年

6
ところで、これに重要な注意事項があるということです方法はありません(あなたが求めるよう)「これを行うには、[W] ithout反復配列全体にわたり」。そのようなループを明示的に(またはなどの関数の背後にstrings.Index)作成すると、コードが何をしているかがより明確になります。私はおそらくPython in array:が何かを速く/魔法のようにやっていると思っているような印象を受けます。私の知る限りではありません。ループを明示的にすると、ライター(およびすべてのリーダー)が他の実装(マップなど)を認識し、考慮するのに役立ちます。
Dave C

5
ただし、セット内の「x」が実際に非常に高速である場合。
Roberto Alsina 2017年

6
プログラム的に高速なアプローチを求めているとは思いません。簡潔なものを求めているだけです(それでもまだ
わかり

回答:


340

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.")
}

4
タイプを指定せずにこれを行う方法はありますか?単一のタイプごとに個別のメソッドを使用せずに、一般的なneedleInHaystack(needle、haystack)関数のみが必要な場合
Allen

25
これは、reflectパッケージを使用して行うこともできますが、かなり非効率的です(おそらく、Pythonのような動的言語で作成した場合と同じくらい遅くなります)。それ以外は、違います。Goにはジェネリックがないと彼らが言うとき、それは人々が意味することです。
andybalholm

2
この答えを見つけた人は、マップをソートできないことに注意する必要があります。goマップを使用することの大きな欠点。
RisingSun 2015年

4
JavaScriptでもマップ(オブジェクト)を並べ替えることはできません。オブジェクトがアルファベット順にソートされた値で値を返すのはv8のバグです。
kumarharsh 2016

7
マップはほとんどの言語でソートされていません-これはマップデータ構造(ハッシュマップ)のコースと同等です。
Hejazzman

100

リストに静的な値が含まれている場合の別の解決策。

例:有効な値のリストから有効な値を確認する:

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

11
ええ、もしあなたの「有効な値」がデータベースから来たらどうでしょう?
Rami Dabain 16

ええ、これは素晴らしいですが、これらの値を事前に定義できる場合のみです。
piggybox 2018

2
@RonanDejheroを使用すると、WHERE:myValue IN(サブクエリ):)を使用できます
anilech

3
これは、トップの回答と比べると
すてきです

50

これは、「囲碁のプログラミング: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)
}

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


10
これは、繰り返し検索を行う場合にのみ意味があります。それ以外の場合は、並べ替えとバイナリ検索の複雑さはn * log(n)* log(n)ですが、線形検索の場合はnだけです。
クリスチャン

1
n*log(n) + log(n)2つの結果として生じる独立した操作であるため、実際にはただの
pomo_mondreganto

27

上記のソートの例は近いですが、文字列の場合は単に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


2
この回答は、この回答の前に発生した、以下の回答のあまり有益ではないコピーペーストのように見えます。
キチヌス2017

@cytinusどんな答えを言っているのですか?それは私がに基づいて私が見る唯一のものsort.SearchStringsです。
akim

1
大きなスライスを繰り返し検索すると、100倍のスピードアップが得られました。
Xeoncross

20

同様の質問があり、このスレッドの提案のいくつかを試すことにしました。

私は、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ステートメントが適しています。


Goがこのケースを最適化するかどうかはわかりませんが、リスト/マップの初期化をテスト関数の外に移動すると違いがありますか?
w00t

ソートされたリストで比較がどのように行われるか、そしてソートがそれに値するかどうかを知りたいと思っています
Michael Draper

switchステートメントの:代わりにどう,ですか?速くなりますか?
Thomas Sauvajon

1 caseつのケースではなく、複数のステートメントを使用してみました。結果は、どちらの関数でもほぼ同じです。
Thomas Sauvajon

7

別のオプションは、マップをセットとして使用することです。キーのみを使用し、値が常に真であるブール値のようなものにする。次に、マップにキーが含まれているかどうかを簡単に確認できます。これは、セットの動作が必要な場合に役立ちます。値を複数回追加した場合、その値はセット内で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)
        }
    }
}

ここは外出先の遊び場です


0

これは、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が持っているものと持っていない一般的な言語構成を尋ねて学ぶことです。それは良い運動です。この答えが愚かであるか有用であるかは読者次第です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.