web-dev-qa-db-ja.com

慣用的なGoのCの3項演算子と同等のものは何ですか?

C/C++(およびそのファミリーの多くの言語)では、条件に応じて変数を宣言および初期化するための一般的な慣用句では、3項条件演算子を使用します。

int index = val > 0 ? val : -val

Goには条件演算子がありません。上記と同じコードを実装するための最も慣用的な方法は何ですか?私は次の解決策を見つけました、しかしそれはかなり冗長に思えます

var index int

if val > 0 {
    index = val
} else {
    index = -val
}

もっと良いものはありますか?

222
Fabien

指摘されたように(そしてうまくいけば驚くことではないが)、if+elseを使うことはGoで条件付きをするための 慣用的な方法 です。

本格的なvar+if+elseコードブロックに加えて、このスペルもよく使われます。

index := val
if val <= 0 {
    index = -val
}

int value = a <= b ? a : bと同等の十分に反復的なコードブロックがある場合は、それを保持する関数を作成できます。

func min(a, b int) int {
    if a <= b {
        return a
    }
    return b
}

...

value := min(a, b)

コンパイラーはそのような単純な関数をインライン化するので、高速、明瞭、そして短くなります。

183

No Goは三項演算子を持っていません。if/else構文を使用するのは慣用法です: http://golang.org/doc/faq#Does_Go_have_a_ternary_form

55
ishaaq

次の三項式があるとします(C言語)。

int a = test ? 1 : 2;

Goでの慣用的なアプローチは、単純にifブロックを使うことです。

var a int

if test {
  a = 1
} else {
  a = 2
}

しかし、それはあなたの要求に合わないかもしれません。私の場合は、コード生成テンプレート用のインライン式が必要でした。

私はすぐに評価された無名関数を使いました:

a := func() int { if test { return 1 } else { return 2 } }()

これにより、両方の分岐が同様に評価されないようになります。

45
Peter Boyer

マップの三項は括弧なしで読みやすいです。

c := map[bool]int{true: 1, false: 0} [5 > 4]
29
user1212212
func Ternary(statement bool, a, b interface{}) interface{} {
    if statement {
        return a
    }
    return b
}

func Abs(n int) int {
    return Ternary(n >= 0, n, -n).(int)
}

これはif/elseよりも性能が上がらず、キャストが必要ですが機能します。参考:

ベンチマークAbbsTernary-8 100000000 18.8 ns/op

BenchmarkAbsIfElse-8 2000000000 0.27 ns/op

7
Phillip Dominy

すべてあなたの枝が副作用をするか、または計算上高価である場合意味的に保存されたリファクタリング

index := func() int {
    if val > 0 {
        return printPositiveAndReturn(val)
    } else {
        return slowlyReturn(-val)  // or slowlyNegate(val)
    }
}();  # exactly one branch will be evaluated

通常はオーバーヘッドはありません(インライン)、そして最も重要なことに、一度だけ使用されるヘルパー関数で名前空間を乱雑にすることなく(読みやすさと保守を妨げる) 実例

単純に Gustavoのアプローチ を単純に適用したのであれば注意してください。

    index := printPositiveAndReturn(val);
    if val <= 0 {
        index = slowlyReturn(-val);  // or slowlyNegate(val)
    }

あなたは異なる振る舞いを持つプログラムを手に入れるでしょう。 val <= 0プログラムが正ではない値を表示しますが、そうではありません! (同様に、分岐を逆にした場合、不必要に遅い関数を呼び出すことでオーバーヘッドが発生します。)

4
eold

eoldの答えは面白くて創造的で、おそらくさらに賢いかもしれません。

ただし、代わりに次のようにすることをお勧めします。

var index int
if val > 0 {
    index = printPositiveAndReturn(val)
} else {
    index = slowlyReturn(-val)  // or slowlyNegate(val)
}

はい、どちらも基本的に同じアセンブリにコンパイルされますが、このコードは、最初に変数に書き込まれた可能性のある値を返すためだけに無名関数を呼び出すよりもはるかに読みやすくなります。

基本的に、シンプルで明確なコードはクリエイティブなコードより優れています。

また、Goはマップがまったく軽量ではないため、マップリテラルを使用するコードは推奨されません。 Go 1.3以降、小さなマップに対するランダムな反復順序は保証されており、これを強制するために、小さなマップに対してはメモリ効率がかなり低くなっています。

その結果、多数の小さなマップを作成および削除することは、スペースと時間がかかります。私は小さな地図を使ったコードをいくつか持っていました(2つか3つのキーがありますが、一般的な使用例は1つのエントリだけでした)。同じコードをデュアルスライスキー[index] => data [index] mapを使って書き直すよりも少なくとも3桁遅くなります。そしてもっとありそうです。以前は実行に数分かかっていた操作の一部として、ミリ秒で完了し始めました。

3
Cassy Foesch