ワンライナーif elseステートメントを行う方法は?


104

PHPで行うように、go(golang)で変数を代入する単純なif-elseステートメントを記述できますか?例えば:

$var = ( $a > $b )? $a: $b;

現在、私は以下を使用する必要があります:

var c int
if a > b {
    c = a
} else {
    c = b
}

申し訳ありませんが、この制御ステートメントと、サイト内またはGoogle検索で情報が見つからなかった場合、名前を思い出せません。:/


4
これは三項演算子と呼ばれています...いいえ、Goにはありません。
Simon Whitehead

6
あなたが探している言葉は「三項」だと思います
BenjaminRH


10
明確にするために、三項演算子はアリティ3の任意の演算子、つまり3つの部分式をバインドする任意の演算子です。Cにはたまたまそのような演算子が1つしかありません。そのため、通常は三項演算子と呼ばれます。ただし、本名は「条件演算子」です。
thwd

回答:


131

コメントで述べたように、Goは3成分のワンライナーをサポートしていません。私が考えることができる最も短い形式はこれです:

var c int
if c = b; a > b {
    c = a
}

しかし、それを行わないでください。それだけの価値はなく、コードを読んだ人を混乱させるだけです。


83
@thoroc実生活ではそれをおそらく回避するでしょう。直感的に理解できないIMHOなので、読みやすさを下げるために2行を保存する価値はありません。
Not_a_Golfer 2014年

14
@ thoroc、Not_a_Golferが言ったことを聞いてください。自慢しないで、保守可能なソフトウェアを書くように努めるべきです。きちんとしたトリックはきちんとしていますが、コードを読んでいる次の人(数か月/数年であなたを含む)はそれらを高く評価しません。
kostix 2014年

3
読みやすさによって異なります。これはもっと読みやすいと思いますが、他と同様に、悪用される可能性があります。ifブロックの外で不要な変数を宣言する必要がある場合、これが勝者だと思います。
arjabbar 2015年

@Not_a_Golfer対称性による読みやすさだけでなく、意図との構造的コントラストもコンパイルされたコードにあり、冗長な初期化のために追加コストがかかる場合もあります。
ウルフ

5
ああ、喜ばしいことです。三項演算子がないことでGoがどれだけ得られるかを見てください。可読性のコメントは別として、1つのブランチのみが実行される遅延評価された構成を、最初のブランチが常に実行され、2番目のブランチがさらに実行される可能性がある構成に変更しました。
itsbruce 2017

29

私はよく以下を使用します:

c := b
if a > b {
    c = a
}

基本的に@Not_a_Golferと同じですが、型推論を使用します。


4
同じ欠点があり、明らかに対称的な要件に対して非対称ソリューションを使用する場合、理解が複雑になります。
ウルフ

2
そして、2つのブランチのうちの1つだけが常に評価される1つに、場合によっては両方が使用される、遅延評価されたコンストラクトを回すのと同じ欠点(両方の場合、最初の評価は冗長でした)
itsbruce

1
ユースケースによっては、これはまだ非常に役立つかもしれません。例:listeningPath := "production.some.com"; if DEBUG { listeningPath := "development.some.com" }プロダクション用のternaryと同じ速度で、読みやすさはかなり良いです。
Levite 2017年

私は通常、意図的にCPUサイクルを無駄にすると泣きます。
alessiosavi

28

他の人が述べたように、Goは三元のワンライナーをサポートしていません。しかし、私はあなたがあなたが望むものを達成するのを助けることができる効用関数を書きました。

// IfThenElse evaluates a condition, if true returns the first parameter otherwise the second
func IfThenElse(condition bool, a interface{}, b interface{}) interface{} {
    if condition {
        return a
    }
    return b
}

これを使用する方法を示すテストケースをいくつか示します

func TestIfThenElse(t *testing.T) {
    assert.Equal(t, IfThenElse(1 == 1, "Yes", false), "Yes")
    assert.Equal(t, IfThenElse(1 != 1, nil, 1), 1)
    assert.Equal(t, IfThenElse(1 < 2, nil, "No"), nil)
}

楽しみのために、次のような便利なユーティリティ関数を作成しました。

IfThen(1 == 1, "Yes") // "Yes"
IfThen(1 != 1, "Woo") // nil
IfThen(1 < 2, "Less") // "Less"

IfThenElse(1 == 1, "Yes", false) // "Yes"
IfThenElse(1 != 1, nil, 1)       // 1
IfThenElse(1 < 2, nil, "No")     // nil

DefaultIfNil(nil, nil)  // nil
DefaultIfNil(nil, "")   // ""
DefaultIfNil("A", "B")  // "A"
DefaultIfNil(true, "B") // true
DefaultIfNil(1, false)  // 1

FirstNonNil(nil, nil)                // nil
FirstNonNil(nil, "")                 // ""
FirstNonNil("A", "B")                // "A"
FirstNonNil(true, "B")               // true
FirstNonNil(1, false)                // 1
FirstNonNil(nil, nil, nil, 10)       // 10
FirstNonNil(nil, nil, nil, nil, nil) // nil
FirstNonNil()                        // nil

これらのいずれかを使用する場合は、https://github.com/shomali11/utilで見つけることができます


12

正解をご指摘いただきありがとうございます。

私はGolang FAQ(duh)をチェックしたところ、明確に述べられています、これは言語では利用できません:

Goには?:演算子はありますか?

Goには三元形式はありません。以下を使用して同じ結果を得ることができます。

if expr {
    n = trueVal
} else {
    n = falseVal
}

主題に興味があるかもしれない追加情報が見つかりました:


1行でも可能var c int; if a > b { c = a } else { c = b }です。しかし、読者のレクリエーションのためのライトブロックを形成するために、5行に保つことをお勧めします;)
Wolf

7

マップを使用してちょうど1行でこれを行うための1つの可能な方法、簡単な私がいるかどうかチェックしていますa > bそれがあるならばtrue、私は割り当てていますcaそうb

c := map[bool]int{true: a, false: b}[a > b]

ただし、これはすばらしいように見えますが、場合によっては、評価順序が原因で完全なソリューションにならないことがあります。例えば、私は、オブジェクトがされていないかどうかをチェックしていた場合nil、それから、次のコードスニペットの意志を見て、いくつかのプロパティを取得panicする場合のをmyObj equals nil

type MyStruct struct {
   field1 string
   field2 string 
}

var myObj *MyStruct
myObj = nil 

myField := map[bool]string{true: myObj.field1, false: "empty!"}[myObj != nil}

なぜなら、条件を評価する前にマップが最初に作成および構築されるため、myObj = nilこの場合は単にパニックになります。

条件を1行だけで実行できることを忘れないでください。以下を確認してください。

var c int
...
if a > b { c = a } else { c = b}

4

三項演算子の代わりにラムダ関数を使用する

例1

最大整数を与える

package main

func main() {

    println( func(a,b int) int {if a>b {return a} else {return b} }(1,2) )
}

例2

must(err error)エラーを処理するこの関数があり、条件が満たされていないときにこの関数を使用するとします。(https://play.golang.com/p/COXyo0qIslPでお楽しみください

package main

import (
    "errors"
    "log"
    "os"
)

// must is a little helper to handle errors. If passed error != nil, it simply panics.
func must(err error) {
    if err != nil {
        log.Println(err)
        panic(err)
    }
}

func main() {

    tmpDir := os.TempDir()
    // Make sure os.TempDir didn't return empty string
    // reusing my favourite `must` helper
    // Isn't that kinda creepy now though?
    must(func() error {
        var err error
        if len(tmpDir) > 0 {
            err = nil
        } else {
            err = errors.New("os.TempDir is empty")
        }
        return err
    }()) // Don't forget that empty parentheses to invoke the lambda.
    println("We happy with", tmpDir)
}

2
興味深いアプローチです。より複雑な場合に役立ちます。:)
thoroc

1

場合によっては、定義と割り当てが同じ行で行われるように無名関数を使用しようとします。以下のように:

a, b = 4, 8

c := func() int {
    if a >b {
      return a
    } 
    return b
  } ()

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


0

非常に類似した構文が言語で利用可能です

**if <statement>; <evaluation> {
   [statements ...]
} else {
   [statements ...]
}*

*

すなわち

if path,err := os.Executable(); err != nil {
   log.Println(err)
} else {
   log.Println(path)
}

0

これにはクロージャーを使用できます。

func doif(b bool, f1, f2 func()) {
    switch{
    case b:
        f1()
    case !b:   
        f2()
    }
}

func dothis() { fmt.Println("Condition is true") }

func dothat() { fmt.Println("Condition is false") }

func main () {
    condition := true
    doif(condition, func() { dothis() }, func() { dothat() })
}

Goのクロージャー構文で私が持っている唯一の不満は、デフォルトのゼロパラメーターゼロの戻り関数にエイリアスがないことです。それにより、はるかに優れたものになります(型名だけでマップ、配列、およびスライスリテラルを宣言する方法を考えてください)。

または、より短いバージョンでさえ、コメンターが提案したように:

func doif(b bool, f1, f2 func()) {
    switch{
    case b:
        f1()
    case !b:   
        f2()
    }
}

func dothis() { fmt.Println("Condition is true") }

func dothat() { fmt.Println("Condition is false") }

func main () {
    condition := true
    doif(condition, dothis, dothat)
}

関数にパラメーターを与える必要がある場合でも、クロージャーを使用する必要があります。これは、パラメーターがメソッドに関連付けられた構造体であると考える関数だけではなく、メソッドを渡す場合に回避できます。


短いバージョンdoif(condition, dothis, dothat)
vellotis

1
はい、それはさらに短くなり、関数だけを渡します。1つのユーティリティライブラリでこの関数を1回持つだけで、コード全体で使用できます。
Louki Sumirniy
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.