Go: 提案:Go 2:三項条件演算子を追加する

作成日 2019ĺš´07月18日  Âˇ  78コメント  Âˇ  ソース: golang/go

私はここhttps://golang.org/doc/faq#Does_Go_have_a_ternary_formでのGoの慣習と言語の設計者の議論に同意せず、それは言語に本当に欠けている機能だと思います。

Cで次のコードを考えてみましょう。

printf("my friend%s", (nbFriends>1?"s":""));

またはC++の場合:

std::cout << "my friend" << (nbFriends>1?"s":"") << std::endl;

Goでは、間違いを引き起こす可能性のある大量の繰り返し、または非常に冗長で非効率的なコード、あるいはその両方が発生します。

解決策1:

// horribly repetitive, risk of divergence between the two strings
if nbFriends > 1 { 
  fmt.Printf("my friends\n") 
} else { 
  fmt.Printf("my friend\n")
}

解決策2:解決策2:

// difficult to read
fmt.Printf("my friend")
if nbFriends > 1 { fmt.Printf("s") }
fmt.Printf("\n")

解決策3:

// difficult to read
var plural = ""
if nbFriends > 1 { plural = "s" }
fmt.Printf("my friend%s\n", plural)

解決策4:

// dangerous (ifTrue and ifFalse are both evaluated, 
// contrary to a real ternary operator), 
// and not generic at all (works only for strings)
func ifStr(condition bool, ifTrue string, ifFalse string) string {
  if condition { 
    return ifTrue
  }
  return ifFalse
}
fmt.Printf("my friend%s\n", ifStr(nbFriends > 1, "s", ""))

解決策5:

// horrible to read, probably inefficient
fmt.Printf("my friend%s\n",
        func(condition bool) string {
            if condition {
                return "s"
            }
            return ""
        }(nbFriends > 1))
Go2 LanguageChange Proposal Proposal-FinalCommentPeriod

最も参考になるコメント

この例は、1文字程度しかないことを考えると、ちょっと悪い例だと思います。私の意見では、実際には読みにくいです(?: "s": "")
しかし、私は三項演算子を追加する必要があることに同意します。
そして私は個人的に私に役立つであろういくつかの例を作りました、そして私はあなたがそれらのいくつかにいくらか関係することができると思います。

const PORT = production ? 80 : 8080
それ以外の

const PORT = -1
if production {
    PORT = 80
else {
    PORT = 8080
}

fmt.Printf("Running %s build!", production ? "Production" : "Debug") .. etc

しかしもちろん、三項演算子を学ぶのは非常に困難です。

全てのコメント78件

@gopherbot 、ラベルGo2、LanguageChangeを追加

#31659および#32860も参照してください。

ご存知のように、この決定はすでにFAQに記されています。 FAQの回答を変更する必要があると主張したい場合は、いくつかの例が必要です。 これらの例をすでに見て、検討したことをお約束します。 必要なのはデータです。条件演算子を追加することで、コードがよりシンプルで読みやすくなる実際のプログラムです。 また、特にネストされている場合にコードが読みにくくなるなど、条件演算子に関する一般的な懸念に反対する引数も必要です。

また、マイナーな点ですが、英語でのみ機能し、メッセージ文字列のローカリゼーションをサポートしていないため、この例は適切ではありません。

@ianlancetaylor

#31659で、三項関数を提供するための組み込みのcond関数を使用するという、非常に優れた反対提案であると私が考えたものを作成しました。 これは、真/偽の引数の短絡評価を可能にするために、(ジェネリック関数ではなく)組み込みである必要がありました。 個人的にはそれを致命的な問題とは見なしていませんでしたが、人々がcond関数をネストできる可能性にまだ苦しんでいました。たとえそうしたとしても、Cの三項演算子自体の象形文字よりも読みやすいはずだからです。 。

その提案は現在終了しているので、その提案をさらに追求するつもりですか、それとも別の三部形式を持つという考えを完全に諦めましたか?

私は個人的にその考えをさらに推し進めるつもりはありません。 それは真面目な提案というよりは議論の考えでした。 もちろん、誰かがそれを実際の提案に磨き上げたいかどうかは気にしません。 しかし、受け入れられるためには、それが実際の既存のプログラムをどれだけ単純化するかについて、まだいくつかのデータを見る必要があると思います。

OK、明確にしてくれてありがとう。

三項演算子がどれほど一般的であるかを確認するには、他のCファミリ言語のコードを調べるだけですが、 @ Phrounzが冒頭の投稿で指摘したように、Goコード自体を分析することは困難です。これは、多くの構造が機能するために使用されるためです。その不在の周り。

condのアイデアを使用すると、彼の例は次のようになります。

fmt.Printf("my friend%s\n", cond(nbFriends > 1, "s", ""))

そうは言っても、ジェネリックスを入手した場合、私は個人的に自分のcond関数を作成することに満足し、短絡がないことを考えると、引数の評価が安価な場合にのみ使用します。

私の意見では、 x ? a : bのルールを理解するよりも、より多くのコード(ほんの数行)を書く方が良いと思います。 ifステートメントは冗長に見えるかもしれませんが(確かではありません)、理解しやすいです。

さらに、三項条件演算子は、ネストされた複数のx ? a : bを書き込むときに簡単に悪用される可能性があります。 それを導入することの利点は十分ではありません。

三項演算子は、ワンライナー関数で視覚化するのが簡単だと思います。 それでも、ほとんどの場合、エラー処理を扱います。その場合、関数のエラーまたは最も可能性の低いパスをifでラップして処理することにより、「インデントが少ないほど良い」アプローチに従うほうがよいでしょう。複数の分岐ロジックを持つのとは対照的です。

この例は、1文字程度しかないことを考えると、ちょっと悪い例だと思います。私の意見では、実際には読みにくいです(?: "s": "")
しかし、私は三項演算子を追加する必要があることに同意します。
そして私は個人的に私に役立つであろういくつかの例を作りました、そして私はあなたがそれらのいくつかにいくらか関係することができると思います。

const PORT = production ? 80 : 8080
それ以外の

const PORT = -1
if production {
    PORT = 80
else {
    PORT = 8080
}

fmt.Printf("Running %s build!", production ? "Production" : "Debug") .. etc

しかしもちろん、三項演算子を学ぶのは非常に困難です。

はい、それは特にトップレベルの場合に良い例です。

const production = true

//...

const PORT = production ? 80 : 8080

PORTを初期化するためにinit関数は必要ないためです。

組み込みのcond関数は、 const初期化子として使用できる可能性がありますが、汎用バージョンでは使用できませんでした。

@Terottaja @alanfo

その特定の問題に対する慣用的な解決策は、ビルド制約を使用することだと思います。

@Terottaja

グローバル変数/定数の解決策については、他のコメントを参照してください。

ローカル変数/定数の場合、このコードの平和を書くための慣用的な方法だと思います。

const PORT = -1
if production {
    PORT = 80
else {
    PORT = 8080
}

は:

PORT := 8080
if production {
    PORT = 80
}

constをvarに変更したと主張することもできますが、コンパイラが十分に賢くなかった場合は驚かれることでしょう。

この特定の例では、PORTがconstあるかvarであるかはおそらく問題ではありませんが、より一般的には問題になる可能性があります。 たとえば、配列のサイズを定義するために使用される整数を宣言している場合です。

おそらく、私はこの提案について自分の立場を明確にすべきです。 私は個人的に「標準」の三項演算子に問題はありませんが、その形式でGoに導入するのが良い考えかどうかはわかりません。 代わりに、はるかに読みやすいcondビルトインの方がはるかに好きですが、現実的には、どちらかが採用される可能性はほとんどありません。

@Terottaja

グローバル変数/定数の解決策については、他のコメントを参照してください。

ローカル変数/定数の場合、このコードの平和を書くための慣用的な方法だと思います。

const PORT = -1
if production {
  PORT = 80
else {
  PORT = 8080
}

は:

PORT := 8080
if production {
  PORT = 80
}

_const_を_var_に変更したと主張することもできますが、コンパイラーが_賢くなかった場合は驚きます™__PORT_は定数であるため、IMOは実際のコードに違いはありません。

コードにこの種のものがたくさんある場合は、この場合、三項演算子の方が間違いなくクリーンになると思います。私の意見です。

私の意見では、 x ? a : bのルールを理解するよりも、より多くのコード(ほんの数行)を書く方が良いと思います。 ifステートメントは冗長に見えるかもしれませんが(確かではありません)、理解しやすいです。

さらに、三項条件演算子は、ネストされた複数のx ? a : bを書き込むときに簡単に悪用される可能性があります。 それを導入することの利点は十分ではありません。

常にそれを悪用する人々がいるでしょう、コードはその必然的に悪用される可能性がありますが、それはあなたに影響を及ぼしますか? ほとんどの場合、

私はこの機能をサポートしていますが、コードの乱用につながる可能性がありますが、一般的なif / elseを避けて三項演算子に置き換えると、コンパイラの最適化に非常に役立ちます。
人々は最初からビット単位のシフトを行ってきました(誰が彼らを責めることができますが、読みやすさの原因でビット単位のシフトを取り除こうとは誰も提案していません)。

@Lexkane :コンパイラには、条件付き移動を使用する最適化がすでにあります。 このような最適化を強制するための言語構造は必要ありません。 たとえば、次のコードは1つを使用します。

func f(x, y int) int {
    r := 3
    if x < y {
        r = 7
    }
    return r
}

条件付き移動が生成されていない特定のインスタンスがあり、それが必要だと思われる場合は、コードで問題を開きます。

仕事でGoとJavascriptを同時に使用しているので、Goプログラムでx ? a : bを書きたいと思ったことは何度もあります。 これらすべてのケースを@ianlancetaylorに表示するために、それを書き留めておく必要があります。 それはすべて本物のプログラムでした。
三項演算子は、私たち全員が学校で(大学でさえも)学ぶものであるため、古典的な形式では、コードを読み書きするための自然な方法です。
単項、2進数、3進数の3種類の演算子があることを私たちは皆学びました。 Goには、IMOの本当の理由がないために1つのタイプがありません。
x ? a : bの両手。

単項、2進数、3進数の3種類の演算子があることを私たちは皆学びました。 Goには、IMOの本当の理由がないために1つのタイプがありません。

しかし、そうしなければならない理由はありません。 「三元」は、「4つの部分で構成されている」という意味の英語の単語です。 同様に、4進演算子または5進演算子も簡単に使用できます。

個人的には、三項演算子はデフォルトで読むのが面倒だと感じています。 単項演算子と二項演算子を使用すると、すべてがどこにあるかを簡単に確認できますが、三項演算子を使用すると、特にネストを開始すると、何がどうなるかが常に明確になるとは限りません。 特定の状況ではそれらがよりクリーンであるという議論を見ることができますが、それらの状況以外では、ほとんどの場合、それらはより悪いものです。 gofmtは、潜在的には役立つ可能性がありますが、コードを再フォーマットする方法について、それが実際よりもはるかに積極的である場合に限ります。 ネストを禁止したり、チェーンしたりするなど、ある種の制限されたものが導入される可能性がありますが、その時点では、それが本当に価値があるかどうかはわかりません。

最も単純な演算子のセットで混乱を引き起こすことができるとすでに言われています。 確かに、GoコードはJavaやJavascriptコードよりも読み取りと書き込みが簡単です。 しかし、それを読めなくすることは不可能ではありません。
したがって、三項演算子は主にワンライナー用であり、そのような場合に使用する必要があります。
それ以外の場合は、「if --then --else」を使用して、それを数回ネストし、コードを完全に混乱させることができます。 それは常にあなた次第です。
人々は、コードでワンライナー式が発生する頻度を過小評価していると思います。 まれな場合もありますが、記述されたコードの半分を埋める場合もあります。後で、できればJavascriptの形式で三項演算子を使用したい場合もあります。

私はGoでそれが好きです。制御フローは通常、式ではなくステートメントで行われます(関数の呼び出しは明らかな例外です)。 多くの人気のある言語は「すべてが表現である」ことを目指しており、それはしばしば楽しいものですが、imoは「賢い」コードを書くことを奨励しています。 Go、私にとっては、賢さを最小限に抑えることがすべてです。

ちなみに、三項式を実装する別の(グロス)方法は次のとおりです。

map[bool]string{true: "", false: "s"}[nbFriends == 1]
map[bool]string{true: "", false: "s"}[nbFriends == 1]

素晴らしいトリックですが、単純な有名な広告よりもはるかに「賢い」明らかな? :です。

いくつかの古典的な機能は有害である可能性がありますが、私たちはすでにそれらに慣れています。 気づいていません。

条件はブール値でなければならないので、時々私たちは書く必要があります

x := 0
y := x != 0 ? 1 : 2

直感的ではありません。 ifステートメントでは、一見ifが表示され、条件が存在することがわかっているためです。

三項式では、 ?が表示されている場合にのみ、それが条件であることがわかります。 複雑な場合は、驚いて戻って状態をもう一度読みます。

読み取りフローを左から右、上から下に分割します。

_const_を_var_に変更したと主張することもできますが、コンパイラーが_賢くなかった場合は驚きます™__PORT_は定数であるため、IMOは実際のコードに違いはありません。

まあ、それは恒常性を失うことによって違いを生みます。 Constは最適化だけではありません。 さらに下のコードは、値を混乱させる可能性があります

どうですか:三項は低くなりますが、ネストはできません。
私はそれが欲しいです。

something ? foo : barが読みやすい唯一の理由は、他の言語での使用にすでに慣れているためです。 しかし、決してそれがより読みやすく、より明確であることはありません

if something {
  foo
} else {
  bar
}

特にGoが第一言語である新参者のために。

読みやすさが良くない場合、これから得られる唯一の利点は、コードの行数を減らすことです。 私には、それは私たちがすでに持っているものに対してあまり直感的ではない代替の構成を導入するのに十分な理由のようには思えません。
すでにifがあります。 同じことを行うのに直感的でない別の方法を追加するのはなぜですか?

if something {
  foo
} else {
  bar
}

'else'はまた、視線と読みやすさを壊します(条件が戻った場合は、戻って読み直す必要があります)。 '?'を優先して'else'を削除したほうがよいでしょう。

something ? foo : barが読みやすい唯一の理由は、他の言語での使用にすでに慣れているためです。 しかし、決してそれがより読みやすく、より明確であることはありません

if something {
  foo
} else {
  bar
}

まあ、それはいくつかの点でより明確です:

  • それは本質的に些細な操作であるもののための儀式を減らします、
  • 1行で1つの割り当てを表します。
  • これにより、変数を「定数」として保持できます(したがって、精神的な負担が軽減され、後で変数をいじる可能性が低くなります)。
  • 5行少ないため、より多くのコードを簡単に取り込むことができます。

特にGoが第一言語である新参者のために。

なぜ言語はその表現力を新参者に集中させるのでしょうか? それは、コンピュータの読み書きができない人だけのために単純なUIを作成するようなものであり、したがって、特定のポイントを超えて余分な力を逃す人を苛立たせます。

@bugpowder三項式は完全に置き換え可能であり、 ifステートメントはより直感的で、より表現力があり、より一般的です。

@bugpowder三項式は完全に置き換え可能であり、 ifステートメントはより直感的で、より表現力があり、より一般的です。

のような場合はどうですか。

println("Example bool is: ", bool ? "true" : "false")
これは、コードを次のように見せたい場合を除いて、 ifステートメントを実際に使用できない場合に非常に役立ちます。

bool := "false" if bool { bool = "true" } println("Example bool is: ", bool ? "true" : "false")

something ? foo : barが読みやすい唯一の理由は、他の言語での使用にすでに慣れているためです。 しかし、決してそれがより読みやすく、より明確であることはありません

if something {
  foo
} else {
  bar
}

特にGoが第一言語である新参者のために。

読みやすさが良くない場合、これから得られる唯一の利点は、コードの行数を減らすことです。 私には、それは私たちがすでに持っているものに対してあまり直感的ではない代替の構成を導入するのに十分な理由のようには思えません。
すでにifがあります。 同じことを行うのに直感的でない別の方法を追加するのはなぜですか?

私がコーディングを学んだとき、三項演算子は最初は奇妙で複雑に見えましたが、さらに調べて、次のような例を見た後、次のようになりました。

true ? "it's true!" : "It's false!"
それは本当に論理的で単純に見えましたが、それは私だけです。 多分他の人にとってはその超複雑で理解するのは難しいです。

私はここhttps://golang.org/doc/faq#Does_Go_have_a_ternary_formでのGoの慣習と言語の設計者の議論に同意せず、それは言語に本当に欠けている機能だと思います。

しかし、FAQの最後の文は、「なぜ存在しないのか?:」を明確に説明しています。

言語に必要な条件付き制御フロー構造は1つだけです。`

1行でより多くのロジックを絞り込める機能は、まったく新しいものではありません。 制御フローを変更するためのクリーンな方法はすでにありますが、別の方法は過剰です。

三元式の何が問題になっているのか見てみましょう。 単純なケースでは、どちらも問題ありません。

var a, b int
fmt.Println("Example bool is: ", a != 0 && b != 0 ? "true" : "false")
var a, b int
var c string
if a != 0 && b != 0 {
        c = "true"
} else {
        c = "false"
}
fmt.Println("Example bool is: ", c)

物事が複雑になると(条件を1つ追加する)、3項式はあいまいに見えます。

ただし、if-elseフォームがその役割を果たします。 短い形式が必ずしも明確な形式であるとは限らないと思います。

var a, b, c int
fmt.Println("Example bool is: ", a != 0 && b != 0 ? c != 0 ? "true" : "false" : "false")
var a, b, c int
var d string
if a != 0 && b != 0 {
        if c != 0 {
                d = "true"
        } else {
                d = "false"
        }
} else {
        d = "false"
}
fmt.Println("Example bool is: ", d)

三項式は、ifステートメントの特殊なケースにすぎません。 単純な特殊なケースのために新しい構文を追加することは価値がありません。

はい、お願いします、はい! 私はGoが大好きで、読みやすさに重点を置いていますが、非常に冗長なコードとして代償が伴います。
三項演算子は、読みやすさを損なうことなく「書き込み性」を向上させると強く信じています。 これにより、Goのコードが少しエレガントで便利になります。

あいまいさやその他のコーディングの問題について言えば、コーディング慣行が悪い開発者は、とにかく三項演算子なしで悪いコードを書くでしょう。

@ziflex IMHO 、あいまいさを回避するための最良のコーディング方法は、三項式のある言語でも三項式を使用しないことです。

あなたが避けなければならない罠があるので、あなたは次のようなコーディングの実践を学ぶ必要があります

  1. 入れ子にしないでください。
  2. 本当に単純な場合にのみ使用してください。
  3. ロジックが複雑になった場合は、if-else形式で書き直してください。
    ..。

これらの罠はあなたに頭上をもたらすでしょう。

@DongchengWangこれらのプラクティスのいくつかは、単純な「if」ステートメントに簡単に適用できます。

多くの回答者は、三元は交換可能であり、何も新しいものをもたらさないと言います。

しかし同時に、「if err:=maybeError();」を使用している人の数もわかります。 err!= nil {}”構文。これは、単純な「if」ステートメントでも簡単に書き直すことができます。 しかし、何らかの理由で、誰もそれについて本当に不平を言うことはありません。 なんで? 便利だから。 そしておそらくそれは最初からその言語であったからでしょう。

私たちがそれについて議論している唯一の理由は、オペレーターが最初からそこにいなかったという理由だけです。

個人的には、三項式を使用すると、見た目がすっきりし、ネストされたコードが少なくなる場合がいくつかあります。 ほんの少しですが、私はこれに夢中ではありません...

多かれ少なかれ標準の?:文字を使用して構文がさらに複雑になるのを避けるために、次のようにforループの場合のように、既存の言語キーワードを再利用するのはどうでしょうか。

port := 80 if production else 8080

それでも、チェーンして悪用することはできますが、従来のif/elseブロックでできる限りのことです...

この言語が生まれた理由の原則については、まだ瞑想する必要があると思います。単純で、物事を行うための1つの明白な方法、直交する機能、およびライブラリです。

そこは70%くらいだと思います。 すべてが完璧というわけではありません。

これまでのところ、Goについて私が気に入っているのは、コーディングの方法や、各メンバーが使用できるコードスタイルについてのチームメンバー間の議論が少なくなるという事実です(PHPのPSRのさまざまなバージョンを覚えていますか?)。 私たち7人のメンバーからなるチームでは、お互いのコードについて議論することはありません。 私たちは自分たちの目標に焦点を合わせています。

三項条件演算子が好きではないというわけではありませんが、コーディングの方法が引数の1つになるのが好きではないため、Goへの追加などに反対する必要があります。

不要な砂糖。 「他の言語で使われていた」ものを持ち込もうとしている時期のひとつだと思います。 行く感じない。

上記のように、言語に条件演算子がない理由を説明する既存のFAQエントリがあります。 この問題は強力にサポートされていません。 言語に新しい機能を追加しても、それを簡単にすることはできません。 何かを行う2つの方法( ifまたは?: )があると、各プログラマーは使用するフォームを決定しなければならないことがよくありますが、これは良いことではありません。 一般的に、ここでは新しい議論は見られません。

したがって、これはおそらく減少です。 最終的なコメントのために1か月間開いたままにします。

@ziflexの指摘によれば、次の場合に冗長なインラインがどのように行われるかはわかりません。

if bool := operation(); bool {}

三項演算子とは大きく異なります。 これは私が喜んで諦めない構成ですが、次のようなeval ? a : bと同じ複雑さに苦しむ可能性があります。

func main() {
    if a := A(); !B(a) && !C(a) && D(C(a)) {
        fmt.Println("confused")
    }
}

func A() bool {
    return true
}

func B(in bool) bool {
    return !in
}

func C(in bool) bool {
    return in
}

func D(in bool) bool {
    return in
}

これは、1行に記述されていないコードが、上記の例のいくつかと同じように読めなくなる可能性があることを説明するためだけに役立ちます。 構文IMHOを悪用する機能は、非常に有用な演算子が言語に追加されるのを防ぐのに十分な理由ではありません。

ただし、if-elseフォームがその役割を果たします。 短い形式が必ずしも明確な形式であるとは限らないと思います。

var a, b, c int
fmt.Println("Example bool is: ", a != 0 && b != 0 ? c != 0 ? "true" : "false" : "false")

これは悪い例です。括弧を省略したため、例は理解しやすいものの、非常にわかりにくいように聞こえます。

本当に思う
go var a, b, c int fmt.Println("Example bool is: ", (a != 0 && b != 0 ? (c != 0 ? "true" : "false") : "false"))

より読みやすい

var a, b, c int
var d string
if a != 0 && b != 0 {
        if c != 0 {
                d = "true"
        } else {
                d = "false"
        }
} else {
        d = "false"
}
fmt.Println("Example bool is: ", d)

(あなたの例が実際にはさらに単純である可能性があるとしても、それは重要ではないと思います:)

go fmt.Println("Example bool is: ", (a != 0 && b != 0 && c != 0 ? "true" : "false"))

他の式やステートメント内で条件付きが必要になることもありますが、ほとんどの場合、読みやすさが失われると感じています。

私が後悔していることの1つは、両方のブランチがゼロ値にならない条件付き初期化を行う方法がないことです。 コンパイラはおそらくこれを無駄にしないほど賢いことは知っていますが、抽象マシンが何をするかについての私の感覚は、値をゼロにしてすぐに上書きしていることを教えてくれます。 これは...本当に弱い議論です。

FWIW、「Example bool」の例の三部形式が読みやすいとは思いません。特に、現在の私のディスプレイでは、完全な式を表示するために水平スクロールが必要になるためです。 それでも、それが何をしているのかを理解するために、私はもっと頻繁に前後を振り返る必要があります。

いくつかのスクリプト言語からの明白な答えは、ステートメントが割り当て可能な式であるかどうかということです。私は実際にそれらの言語のその設計が本当に好きですが、私はそれをあまり望んでいないと思います。

私はいつもあると思います:

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

...しかし、考えてみると、それを合理化できれば、実際にはこのような状況でかなり使用できるでしょう。 「この関数は実際には単なる表記であり、実際に関数コードを生成する必要はありません。内部ブロックで戻り式を使用する方法が必要です」...

Go言語は単純さのために生まれました。 なぜあなたはこれらの派手なことをしたいのですか? 数行のコードを書いたように感じますが、コードがさらに複雑になり、混乱が生じます。
だから私はサポートしていません

「ファンシー」ではなく、「シンプル」なのでぴったりと収まります。他の多くの言語で使用しているのでおなじみです。 タイピングが少ない一行式なので便利です。 それは私たちがよく使う一般的な構造なので、持っていることが重要です。

次に、Pythonのようなリスト内包表記を使用します。
a if a>1 else b
Rustのように、あらゆる種類の奇妙なシンボルの代わりに。
これらの奇妙な記号を使用してコードを省略するよりも、それを表現するためにより多くのコードを記述したいと思います。
コードは人々が読むためのものです。

Goの本来の意図、つまりシンプルで読みやすいものを破壊しているため、一般的に使用されていない状況では新しい文法関数を追加しないでください。
多くの人は、特定の状況での利便性のために、または他のプログラミング言語によって提供される便利な機能に甘やかされているために、利己的である場合があると思います。Goにお気に入りの機能を追加する必要があります。
私が言いたいのは、Goはあなたの言語ではなく、Goには独自の道があるということです。
あなたはあなた自身の言語を発明することができますが、あなたはそうすることができます。

@YanwenjiepyあなたもGoの「純粋主義者」になり、私の意見では進歩を妨げているようです。

I would rather write more code to express it than to use these strange symbols
それはあなたにとって奇妙かもしれませんが、一般的なCベースの言語に精通している私たちのほとんどにとって奇妙な記号ではありません。 C、C ++、C#、Java、JavaScriptなど。これらはすべて3項式を持っています。

Please don't add new grammar functions for a situation that is not commonly used
三項条件文は実際には非常に便利で、一般的に使用されています。

ちなみに、それは「リスト内包」ではありません。 リスト内包表記は、具体的には[x for y in z] (おそらくプラス条件)のようなものです。 式でのif/elseの使用は別の機能です。

私は三項演算子に精通しており、人生のほとんどで三項演算子を使用している言語を使用していますが、他の言語で見た使用法の約95%は、Goを快適に機能させるものには適さないと思いますin。Goは、式で使用できるpreincrement / postincrement演算子など、ある種の情報密度を回避する傾向があり、三項演算子にも同じ根本的な問題があると思います。 それが何をするのかを考えるにはあまりにも多くのスペースが必要です。

コンパイラはかなり賢いです。 値を宣言し、条件付きで割り当て、1回使用すると、コンパイラーが3項式の場合とほぼ同じように動作することを期待できます。

@Yanwenjiepyあなたも囲碁の「純粋主義者」になっているようですが、私の意見では進歩を妨げています。

I would rather write more code to express it than to use these strange symbols
一般的なC言語に精通している私たちのほとんどにとって、これは奇妙かもしれませんが、奇妙な表記ではありません。C、C ++、C#、Java、JavaScriptなどです。どちらも三項式です。

Please don't add new grammar functions for a situation that is not commonly used
三項条件文は実際には非常に便利で、一般的に使用されています。
たぶん、私も同じような気持ちを持っています。おそらく他の問題については、私は「純粋主義者」ではありません。これは非常に混乱しています。

@seebs確かにその意見を尊重することはできますが、他のCベースの言語から来ている私たちの多くにとって、「働きやすい」とは、親しみやすさと便利さによる生産性を意味します。 Goのi++がi = i + 1よりも快適で便利でない理由を理解できませんでした。特に、ポストインクリメントがループで問題ない場合、たとえばfor i := 0; i < 5; i++ {...}の場合は理解できませんが、ステートメントとしては問題ありません。 私が言うにはあまりにも純粋主義! :)

あなたは何について話していますか? i++は、Goのステートメントとして完全に許容されます。 許可されていないのは、それを_expression_で使用することです。ここでは、値と副作用の両方が評価されます。

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

Cにかなり精通している人として、それでも私は三項演算子がなくても大丈夫だと思います。そして、結果のコードの方が好きです。 Cでも、複数のステートメントが含まれるブロックだけでなく、すべてのブロックで中括弧を使用することについて一貫性を保つようになったのと同じように、これを使用することをやめました。

私はそれ自体でi++を意味するのではなく、 fmt.Printf("%d", i++)のような表現を意味しました。これは私たちの一部にとって便利です。

はい、それは間違いなく便利ですが、かなり明らかに、保守が困難です。 人々はそれを間違え、人々はそれを使っているコードを誤解します。 これはバグの原因であり、単純にそれほど価値がありません。

はい、 x++を実行したい場合は、 Printfの前後に独自のステートメントとして実行する必要があります。 それは確かにコストです。 しかし、代わりに、私は次のことを取得します。

  • Printf呼び出しの一部をコメントアウトしても、xが突然間違った値を取得することはありません。
  • フォーマットメッセージを変更しても、プログラムロジックは壊れません。
  • コードをスキミングしている他の誰か(または十分なコーヒーがない私)は、増分がそこにあることを単に見逃すことはありません。

これはトレードオフですが、かなり良いものだと思います。 プログラミングの初心者向けの質問に答えるのに時間を費やしています。それは、より健康的な言語コミュニティを開発する方法の一部だからです。 私は、Cコードよりも、人々のGoコードの句読点の微妙な問題を解くのに費やす時間がはるかに少なく、微妙なタイプミスによって完全に引き起こされる問題がはるかに少なくなっています。

確かに、これはCを理解または評価していない人々からの純粋主義ではありません。この言語は、単純にそれほど複雑ではないことから多くの価値を得るように思われるため、より多くの余地が残されていると考えられます。コードの解析にそれほど多くの労力を費やしていないため、ロジックで複雑なことを行っています。

私はあなたの言うことを聞きますが、私はそのすべてについて確信していません。 たとえば、次はGoで機能し、あなたの議論によれば、それが唯一の許可された構文であるはずです。

    for i:=0; i<5; i=i+1 {
      fmt.Printf("%d\n", i)
    }

ただし、より一般的で使い慣れた構文も許可されます。

    for i:=0; i<5; i++ {
      fmt.Printf("%d\n", i)
    }

なぜだと思いますか?

私の議論は、実際には最初のものだけが許可されるべきであると述べていません。 私は2番目の方が好きです。インクリメント演算子はスタンドアロンのものであり、副作用ではないため、よりシンプルで読みやすくなっています。

できないことに注意してください:

i := 0
for i++ < 5 {
    ...
}

Goでは、式に割り当てや増分を入れることができないためです。 ご不便をおかけすることもありますが、表現を誤解したり、値の変更に気づかなかったりする頻度は基本的に100%といいですね。

それは私にはあまりにも純粋です:)とにかく、私のポイントは、 i=i+1とi++の両方がループで許可されているので、単一行の利便性を好む人のために3つのバリエーションも許可すると言います、例えば

`` `Go
ポート:=生産? 80:8080

as well as the usual:

```Go
Port := 8080
if production {
    Port = 80
}

単純さについて言えば、前者の方が単純だと思います。

どうですか:

Port := production++

また

fmt.Printf("port: %d\n", production++)

また、Cイディオムを使用すると、Goよりも短くなります。 しかし、どちらの場合も、その複雑さが存在する可能性があるため、プログラム全体を理解するのが少し難しくなると私は主張します。現在、これらの影響に常に注意する必要があります。

より基本的な哲学的レベルで:問題は、あなたの解決策がその「便利さ」を好む人だけのものではないということです。 それはまた、他のすべての人に永遠に強制されます。 誰もが他の人のコードを読まなければならないからです。 したがって、このような機能をオプトアウトすることはできません。 彼らは「まあ、それは私が維持するのが難しいので、私はそれを使用しない」と言うことができず、それに対処する必要はありません。 彼らはそれが彼らの世界の一部であることに固執しています。 他の誰かが取り組んでいた可能性のあるコードを読むときはいつでも、新しい複雑さのセットに注意する必要があります。

実際には、この「単純さ」にはかなりのコストがかかります。これは、実際には単純さではなく、式を短くするだけだからです。 これは、1行のブレースレスifのようなものです。 簡単なように見えますが、2行目が必要であり、中かっこを追加するのを忘れる可能性がゼロではないため、すぐに中かっこを配置するよりも多くの時間が失われます。

あなたが書くとどうなるか知っています:

Port := production ? 80 : 8080

数日後:

Port := production ? 80 : test ? 4080 : 8080

しかし、誰かが2つのブールが悪い選択であることに気づき、それを修正します。

Port := mode == "production" ? 80 : mode == "test" ? 4080 : 8080

そして、それはたった1行であり、 ?:を使用しているため、人々はそれを長くすることは余分な努力であると感じ、それを修正したりクリーンアップしたりしません。 そして今、彼らはそのようにすることに投資しています。

これで、 ?:の操作が15の深さでネストされ、実際のコードで確認できました。これは、絶対にルックアップテーブルである必要があります。

そして、それが最終的には?:操作は15の深さでネストされます。これは、実際のコードで見たものであり、絶対にルックアップテーブルである必要があります。

ただし、15の深さでネストされた「if-else」操作も、絶対にルックアップテーブルである必要があります。

ああ、確かに。

ただし、15の深さのネストされたif / else操作があり、ルックアップテーブルに変換する場合、単一行ソリューションの「単純さ」を失ったようには感じません。

問題は、あなたの解決策がその「便利さ」を好む人だけのものではないということです。 それはまた、他のすべての人に永遠に強制されます

真実が反対なので、私はこれ以上反対することはできませんでした! それを行う方法を課し、速記バージョンを選択する私の自由を制限するのは、実際にはあなたの純粋な見方です。 あなたがそれを使いたくないのなら、それはあなたの選択ですが、私の選択を制限しないでください!

var a string = "freedom" #$の代わりに短縮されたa := "freedom"を書くことを選択できるのであれば、三項代入の自由と利便性が必要です。

Goツールは、コードのフォーマットを標準化するのに最適です。それだけで、他の人のコードを非常に簡単に読み取ることができると思います。

私にとって重要なのは、三項の割り当ては英語に自然に翻訳されるため、読みやすく、理解しやすいということです。 これが他の多くの言語でとても人気がある理由だと思います。 私にとってこれ:

port := production ? 80 : 8080

...次のように変換されます:「この製品ですか?はいの場合、ポートは80であり、いいえの場合、ポートは8080です」
(ネストされている場合でも、単純で単純な単一の割り当て)

port := 8080
if production {
    port = 80
}

これは、「ポートは8080(期間)ですが、これが本番の場合は、ポートを80に変更します」(2番目の割り当て)に変換されます。

2番目は私にとって間違いなく読みやすいものではありません。 行ったことはありません。

それが? そして:それは人々を悩ませています、私は他の単一行の構文にも満足しています。

この1行の構成は、計算されていない初期値に対して機能します。 これを計算された初期値に拡張する方法は素晴らしいでしょう。

v := a; if t { v = b }        // non-computed initial value

v := f(); else if t { v = b } // f() not evaluated where t==true

悲しいことに、_go fmt_は、多くの有用な単一行構造を破壊します。 そのため、私は_gofmt_を使用しません。 読みやすいコンパクトなコードを伸縮すると、読みにくくなります。 しかし、それは接線です。

この機能は、Go 2がジェネリックスを追加する場合、三項演算子なしで実現できます。

func ternary(type T)(cond bool, vTrue, vFalse T) T { 
    if cond { return vTrue } else { return vFalse }
}

もちろん、これを使用することは、特に複数のternary呼び出しがあった場合にはきれいではありません。

評価に関しては、これはコンパイラレベルでの最適化である可能性があります。

この機能は、Go 2がジェネリックスを追加する場合、三項演算子なしで実現できます。

func ternary(type T)(cond bool, vTrue, vFalse T) T { 
    if cond { return vTrue } else { return vFalse }
}

もちろん、これを使用することは、特に複数のternary呼び出しがあった場合にはきれいではありません。

評価に関しては、これはコンパイラレベルでの最適化である可能性があります。

いいえ、vTrueとvFalseは常に評価されます。

ternary(3>2, func1(), func2())

func1()とfunc2()の両方が呼び出されます。 func2()を評価する必要がないことをコンパイラーは知ることができませんでした...そして、関数呼び出しでは、関数の呼び出しの前に引数が常に評価されることが期待されることが基本原則であるため、とにかくそれを想定するべきではありません。自体。 func2()が値を返すことに加えて何かをするなら、私たちはこのことをやりたいです。さもなければ、それは非常に予測不可能で理解するのが難しいでしょう。

(falseの値が原則として評価されることを想定していない実際の三項演算子とは異なります。)
`` `

この機能は、Go 2がジェネリックスを追加する場合、三項演算子なしで実現できます。

func ternary(type T)(cond bool, vTrue, vFalse T) T { 
    if cond { return vTrue } else { return vFalse }
}

もちろん、これを使用することは、特に複数のternary呼び出しがあった場合にはきれいではありません。
評価に関しては、これはコンパイラレベルでの最適化である可能性があります。

いいえ、vTrueとvFalseは常に評価されます。

ternary(3>2, func1(), func2())

func1()とfunc2()の両方が呼び出されます。 func2()を評価する必要がないことをコンパイラーは知ることができませんでした...そして、関数呼び出しでは、関数の呼び出しの前に引数が常に評価されることが期待されることが基本原則であるため、とにかくそれを想定するべきではありません。自体。 func2()が値を返すことに加えて何かをするなら、私たちはこのことをやりたいです。さもなければ、それは非常に予測不可能で理解するのが難しいでしょう。

(falseの値が原則として評価されることを想定していない実際の三項演算子とは異なります。)

その場合、署名は次のようになります。

func ternary(type T)(cond bool, vTrueFunc, vFalseFunc func() T) T { 
    if cond { return vTrueFunc() } else { return vFalseFunc() }
}

私は認めざるを得ませんが、この実装は非常に醜いです:(

これは減少の可能性があると宣言したため(https://github.com/golang/go/issues/33171#issuecomment-525486967)、追加のコメントがありましたが、私たちが知る限り、実質的に新しいことは何も言っていません。 ?:構文が便利な場合があることに同意しますが、全体として、言語に追加する価値はないようです。

--@ golang/proposalの場合-レビュー

それで、決定は「追加する価値がないように思われる」に基づいていますか?
まったく驚かない…三項表現は言語の「純粋さ」を破壊するだろうね?

言語の変更は、常に費用対効果のトレードオフです。 明確な答えはめったにありません。

三項演算子の期間がないことについては簡単な説明はありません。

表面的には、この基本機能の実装を拒否することは、自転車が高速であると危険である可能性があると主張し、結果としてハイギアの自転車を作ることを拒否することに似ています。 Goの言語アーキテクトが正しいか間違っているかについての会話で混乱する人もいるかもしれませんが、抽象化のレベルを上げて、言語が機能の問題と保守性の問題を組み合わせる必要があるかどうかを検討したいと思います。

機能Xが悪用されてコードのネズミの巣になる可能性がある場合、機能Xが言語から除外される十分な理由はありますか?

私はそれがそうであるかもしれないと主張しますが、それ自体ではありません:それは需要と難しさに対して比較検討されるべきです。 機能の需要が高く、実装が容易な場合は、機能を実装し、それを禁止する方法を提示することで懸念を切り離し、デフォルトで禁止することもできます。

実際、需要が十分に高い場合、困難でさえそれを拒否する悪い理由です。 Javascript(ES2015)のclass構文について考えてみましょう。言語のアーキテクトは実際には機能を追加したくありませんでした。構文を追加するのは実際にはかなりの作業でしたが、需要は非常に高かったです。 彼らは何をしましたか? 彼らは構文を追加しました。この機能を望まない組織は、リンティングレベルで構文を簡単に禁止できることを十分に理解していました。

これは、需要を考えると、三項演算子の適切な解決策です。 これらの懸念を切り離すことは適切であり、より構成可能な方法でそれらを解決することが最も理にかなっています。 「未使用の変数」エラーのように、「プログラムが実行される前に、この1つを修正する必要があるプレスを停止する」という危機に陥る問題についても、同じことが起こるはずです。 (はい、 _ソリューションがあることは知っていますが、それでもこれを構成可能にする必要がある場合です)

言語は、実際にその言語を使用している建築家との深いつながりがなくても、よりよく知っている少数の建築家の産物であるべきだと考えるのは誤りです。 言語のアーキテクトが間違っていることを証明するためのデータの要求は称賛に値しますが、そのような分析は不要です。 このスレッドのサイズを見てください。需要があります。

残念ながら、需要を無視することが競合製品につながるのです。この場合、これが言語が分岐する方法です。 フォークしたいですか? (明確にするために:これは予測であり、脅威ではありません。
私は間違いなく言語をフォークしていません。)

@dashこの問題はクローズされており、議論するつもりはありませんが、不実表示であると私が信じていることを訂正したいと思います。 あなたは、Goが「...実際に言語を使用している建築家との深いつながりがなくても、よりよく知っている少数の建築家の製品」である言語であることを暗示しています。 これは確かに真実ではありません。 Goチームの全員、そして確かに「アーキテクト」は毎日Goコードを作成しており、2007年以来かなりの量を作成しています。また、ほぼ毎日、他のGoユーザーとやり取りしています。 私たちは、実際にその言語を使用している人々、つまり私たちと、とりわけ私たちと深いつながりを持っています。

私は建築家ではありません。私はこの言語を多用しています。また、三項演算子が利用可能であれば、ほぼ確実に使用する状況に頻繁に遭遇します。 そして、後でコードを読んで、それについて考えます。そこにないのはうれしいです。 YMMV。

このようなことや、未使用の変数の警告を「構成可能」にすることで、開発者としての私の生活が楽になるとは思いません。 開発者としての私の人生は難しくなると思います。

私も建築家ではありません。私もこの言語を多用しています。また、三項演算子が利用可能であれば、ほぼ確実に使用する状況に頻繁に遭遇します。 そして、後でコードを読んで、この便利な機能を否定する少数の人々を呪います!

ここでも同じですが、私はGoを毎日使用しており、毎日必要になります。これにより、コードがより明確になり、さらに堅牢になると確信しています。

ちなみに「リワードFAQ回答」の提案で

簡潔にするために、条件ステートメント(式ではない)の代わりに三項演算子を使用します。

「簡潔さ」は悪いことのように言われます。 簡潔さは読みやすさを助けます。 読み取り可能なコードの全体的な考え方は、それが実際に行うことの「要点にまっすぐ」であるということです。 これは本番環境であるため、ポートに8080または-1を影響させ、コードの後半で80に影響を与えるのとは異なります。

Goチームが一貫して反対しているだけでなく、(絵文字投票で判断すると)コミュニティの約60%も反対しているため、Goが三項演算子を取得する可能性はほとんどないと思います。

ただし、Goが最終的にジェネリックスを取得する場合、おそらくコンパイラの最適化による場合を除いて、短絡が発生しないにもかかわらず、チームは標準ライブラリに三項関数を追加することを真剣に検討する必要があると思います。

これを行わない場合、ある種の用語演算子/関数(私自身を含む)を支持する40%は、すぐに独自の演算子/関数を作成します。 これにより、異なる名前(Cond、Iff、Iif、Pick、Choose、Ternなど)が選択され、異なる名前のパッケージに含まれるため、読みやすさとメンテナンスの悪夢が生まれます。

代わりに標準ライブラリに追加された場合、この断片化は発生しません。賛成する人は誰でも標準バージョンを使用し、それが気に入らない人は少なくともそれが何をするかを知っているからです。

func ifThen(condition bool、ifTrue、ifelse interface {})interface {} {
条件{
ifTrueを返す
} そうしないと {
ifelseを返す
}
}

三項演算子に関するこの議論は、「別の問題の解決策」に帰着する場合があると私は感じています。

関数にデフォルト値がないため、次のようにコードを記述します。

if elementType == "" {
    elementType = "Whatever"
}
//  times X ...

単純にこれを次のようにしたい人と一緒に:

elementType = elementType == "" ? "Whatever" : elementType
// times X ...

または

func DoDesign( elementType string = "Whatever" )

したがって、三項演算子は、他の問題に関連する問題を解決しようとします。 より標準的なGoバージョンは確かに読みやすくなっていますが、4つまたは5つを続けて処理していると、読みにくくなります。

@ArnoldoRが示すように、人々がますます独自の「ソリューション」を構築し始めたときに読みやすさが提供されるかどうかについても疑問があります。 Javascriptを悩ませた問題の1つは、機能不足の「ソリューション」の増加であり、その結果、一部のプロジェクトでNPMパッケージが左右にインポートされました。 SqlXのような人気のあるパッケージは、Goの機能が不足していることを示しています。

読みやすさは1つのことです。 しかし、多かれ少なかれ20行を書かなければならないことは、5行に含まれる可能性があります。 それはどんなプロジェクトにも積み重なっていきます。

問題が、三項を誤用して読み取り不可能なコード、特にネストされた三項演算子を作成する可能性がある場合は、それに制限を設けます。 三項演算子が「1レベル」のみに制限されていて、コンパイラーが深いレベルの演算子からあなたを止めれば、このトピックのほとんどの人は問題がないと思います。

とにかく、人々がジェネリックスを悪用してそれらを実装しようとしていることを私たちは知っています。 だから、なぜ公式バージョンを提供しないのですか、それならあなたはそれの乱用を制限することができます。 しかし、上記のようなワイルドな関数は、Javascriptコミュニティで見たように、時間の経過とともに人気が高まるでしょう。 そして、魔神がボトルを離れるとき、それを制御することはもうありません。

このページは役に立ちましたか?
0 / 5 - 0 評価