(*'▽') コーディングスタイル、コーディング規約のはなしだよ。
要するに、こういうコード。
if ( hoge == false ) { }
先日Twitterで見掛けた話題
元ネタは、先日Twitterで見掛けたこんな話題です。
まれに、下記のようなコードを書くプロジェクトを見ますが
— 金魚 (@tecPanda001) 2017年10月4日
Java以外の言語ではセオリーだったりするのでしょうか?
if ( flag == false) {
// ・・・・
}
気になったので引用リツイート形式でこんな感じにレスってみた。
あ、これ、ぼくもブログで書きたかった内容だ。
— すがりょー (@sugaryo1224) 2017年10月4日
bool変数と真偽リテラルの比較はぼくも大嫌い。
たしかこれってC++とかの時代のBOOL型(intのtypedef)で、FALSEが-1でTRUEがそれ以外のすべての値(だったかな?)って決まりしか無かった時代の名残だっけ? https://t.co/PWSBSpYTc1
うろ覚えだったので、FALSE/TRUE
の定義値には自信が無かったけど、確かこんな話だったよなぁ、と。
うろ覚えだったので、少し調べつつ補完。
C言語の時代、bool型が言語標準で存在しなかった時代の代用方法の一つ。
でした。
なお、他の代用方法としてはenumを使用するとかもあったようです。
ちなみにぼくは、新人時代にちょっとだけVisualC++とC++/MFCをやっていた経験がありますが、C言語はやった事がありません。
C言語時代のBOOL定義
typedef int BOOL #define FALSE 0 #define TRUE 1
こんな感じで定義されているのが、C言語時代のBOOL型です。
BOOLはintである。
定義を見てわかる通り、厳密にはBOOL型という型は存在せず、実態はintのマクロ定義になっています。
要するに、モノはint値な訳です。
public static final int FALSE = 0; public static final int TRUE = 1;
Javaでこう書いたのと大差ないと言う事。
Javaだと型に別名を与える方法がありませんが、C#であればusingを使ったと思えばこんな感じ。
using BOOL = System.Integer32; public const BOOL FALSE = 0; public const BOOL TRUE = 1;
FALSEの定義は一択だが、TRUEの定義がブレていたらしい
で、モノはintなので、普通に数値として演算出来たり、0/1
以外の値を保持する事が出来たりします。
つまり、BOOL型の値がTRUEでもFALSEでもない値を持つ事がある、と言う事を意味します。(完全に使い方が悪いんですけどね)
また、FALSE = 0
と言うのは共通していても、稀にTRUE = 1
ではない定義をされる事があったそうです。
この為、必ずしもTRUEとの比較が一意に定まらない、と言う問題があったため、
BOOL型の比較の際は == FALSE
若しくは != FALSE
に統一する。
と言うワークアラウンドが定着したそうです。
追記:BOOL型変数がTRUEでもFALSEでもない値を持つ事による弊害の具体例
なぎせさんからコメント頂いたので、具体例を追記します。
typedef int BOOL
かつBOOL FALSE = 0; BOOL TRUE = 1;
である前提で以下のコード。
BOOL flag = 2; // BOOLはintなので普通に代入可能。 if ( flag == TRUE ) // 要するに 2 == 1 の比較なので不成立。 { 真の場合の処理(); } else if ( flag == FALSE ) // 要するに 2 == 0 の比較なので不成立。 { 偽の場合の処理(); } else { // ここには来ないと思った? 残念、来ちゃうんでーす!! }
これはまぁサンプルコードなのですぐ近くにBOOL変数と値が見えているので良いですが、これがBOOL型のパラメータだった場合は、与える側が変な値を与えて来た場合に想定しない処理ルートに入り込むと言う事が有り得ます。 *1
実際の実装で有り得るコードとしてはこんな感じですか。
void とあるメソッド( BOOL flag ) { // ほにゃらら~の時はここで処理終了。 if ( flag == TRUE ) return; // ここから長い処理 フラグが偽の場合だけやりたい処理(); }
なので、BOOL変数の判定では、TRUEかFALSEか
という比較ではなくFALSEか否か
で行うべき、という文化が定着したようです。
上記の「とあるメソッド」をこの方針で書き換えると以下のようになります。
void とあるメソッド( BOOL flag ) { // BOOL型の比較判定にはFALSEか否かでの判定を推奨。 if ( flag != FALSE ) return; // ここから長い処理 フラグが偽の場合だけやりたい処理(); }
「代入と比較を書き間違えたらバグるから定数(代入不能)側を左辺に置く」とかと似たような話ですかね。
大元の話題であるJavaやC#でif ( bool == false )
と言うコードを書いているのは、この文化の名残のようです。
なぎせさんありがとうございます(*'▽')!
現代に残っている false との比較文化
この頃(BOOL型がtypedef int
だった時代)のコーディングスタイルをJavaやC#になった今でも続けているベテランエンジニアが一定数いたり、
或いは、その時代の人が作った古いコーディング規約が改訂されずそのまま転がされてJavaやC#のコーディング規約に転用され、それを見て育った人がいたりして、
真偽型が言語標準で提供されているにも拘らず、未だにこのコーディングスタイルが生き残っている。
と言う話を昔、先輩エンジニアから聞いた記憶があります。
(ぼく自身は現役でのC言語世代じゃないので、伝聞です)
現代に於いて false と比較する事について
先のtweetに自己リプライしたこちらの通り。
ぼくは真偽反転(!演算子)派だけど、
— すがりょー (@sugaryo1224) 2017年10月4日
falseとの比較はまぁビックリマークよりは見易いので明示的にやっている、と言う意味で許容範囲だと思う。
ただ、trueとの比較に関してはもう何の意味もないと思ってるので、それはやってほしくないコーディングスタイルかな。
要するに
if ( !isHogeHoge ) return;
よりも
if ( isHogeHoge == false ) return;
の方が、見落としによる些細なバグを防げると言う利点がある、と言う事。
まぁ、一応、解らなくもない話です。
この「見落とし」ってのがどういう状況で混入するのか良く解りませんけどね。
falseとの比較について
ぼく個人の好みで言えば、既に真偽値なのに真偽判定の比較演算子に食わせるのは無駄処理だと思いますが、
かと言ってその程度の冗長処理が処理速度に致命的な影響を与える事もないので、
後は可読性と理解し易さを優先して良いとは思います。
ただ、この「理解し易さ」と言うのも人によりけりなので難しい所ですね。
ちなみにぼくは false
との比較は解り易いとは思いませんし、!=
だと尚更混乱します。
trueとの比較について
対して、trueと明示的に比較する事は特に何の意味も利点もないので、こっちに関しては反対です。
それでも敢えて理由付けをするなら「false
と比較するコードとの対照性を持たせる為」でしょうか。
bool変数と真偽値リテラルの比較に対する代案
ぼくならむしろtrue/false
との比較ではなく、いっそのこと真偽値を反転した値を説明用変数に受けて、そいつにいい名前を付ける、と言う方法を採りますかね。
完全に人間にとっての可読性を考えて冗長的にするのであれば、説明用変数を使い、if文
自体はシンプルに保つ。
同じ冗長手段ならこっちの方がオススメだと思ってます。
処理上不要な変数が増えるのは嫌だと思うかも知れませんが、普段からメソッドはシンプルな単位で小さく分割しているので、説明用変数のひとつふたつ増えたからと言って問題ありません。 というか、説明用変数を増やす余地すらないようなメソッドは、既に分割するべきレベルで大きいと考えます。
結論
最終的にこういうコーディングスタイルに関しては「宗教論争」であって正解など存在しないから、
— すがりょー (@sugaryo1224) 2017年10月4日
「それぞれの現場の開発規約に従え」というのが結論にしかならないと思うけどね。
是非に対する議論は面白いからそれはそれとして、おまいら、コーディング規約は守ろうな!!
あと、コーディング規約は陳腐化しないように、時流に合わせてきちんとメンテしような!!
*1:まぁ、実際にはTRUEでもFALSEでもない意味不明な2と言う値を食わせた奴が悪い、って話もあるんですけどね。そもそもその不適切な値がどういうルートで渡って来たのかは別途調べる必要があります。