ようこそここは俺のチラシの裏だ。

専門学校卒のぽんこつえんじにあが個人事業主になって書いているただの日記。

JavaやC#で真偽値をfalseと比較判定する事について。

(*'▽') コーディングスタイル、コーディング規約のはなしだよ。

要するに、こういうコード。

if ( hoge == false ) {

}

f:id:sugaryo1224:20171005011304j:plain

先日Twitterで見掛けた話題

元ネタは、先日Twitterで見掛けたこんな話題です。

気になったので引用リツイート形式でこんな感じにレスってみた。

うろ覚えだったので、FALSE/TRUEの定義値には自信が無かったけど、確かこんな話だったよなぁ、と。

うろ覚えだったので、少し調べつつ補完。

C言語の時代、bool型が言語標準で存在しなかった時代の代用方法の一つ。

でした。

なお、他の代用方法としてはenumを使用するとかもあったようです。

(*'▽') enumってC言語の時代からあったのかー。

ちなみにぼくは、新人時代にちょっとだけ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;

  // ここから長い処理
  
  フラグが偽の場合だけやりたい処理();
}

「代入と比較を書き間違えたらバグるから定数(代入不能)側を左辺に置く」とかと似たような話ですかね。

大元の話題であるJavaC#if ( bool == false )と言うコードを書いているのは、この文化の名残のようです。

なぎせさんありがとうございます(*'▽')!

現代に残っている false との比較文化

この頃(BOOL型がtypedef intだった時代)のコーディングスタイルをJavaC#になった今でも続けているベテランエンジニアが一定数いたり、

或いは、その時代の人が作った古いコーディング規約が改訂されずそのまま転がされてJavaC#のコーディング規約に転用され、それを見て育った人がいたりして、

真偽型が言語標準で提供されているにも拘らず、未だにこのコーディングスタイルが生き残っている。

と言う話を昔、先輩エンジニアから聞いた記憶があります。
(ぼく自身は現役でのC言語世代じゃないので、伝聞です)

現代に於いて false と比較する事について

先のtweetに自己リプライしたこちらの通り。

要するに

if ( !isHogeHoge ) return;

よりも

if ( isHogeHoge == false ) return;

の方が、見落としによる些細なバグを防げると言う利点がある、と言う事。

まぁ、一応、解らなくもない話です。

この「見落とし」ってのがどういう状況で混入するのか良く解りませんけどね。

falseとの比較について

ぼく個人の好みで言えば、既に真偽値なのに真偽判定の比較演算子に食わせるのは無駄処理だと思いますが、

かと言ってその程度の冗長処理が処理速度に致命的な影響を与える事もないので、

後は可読性と理解し易さを優先して良いとは思います。

ただ、この「理解し易さ」と言うのも人によりけりなので難しい所ですね。

ちなみにぼくは false との比較は解り易いとは思いませんし、!= だと尚更混乱します。

trueとの比較について

対して、trueと明示的に比較する事は特に何の意味も利点もないので、こっちに関しては反対です。

それでも敢えて理由付けをするならfalseと比較するコードとの対照性を持たせる為」でしょうか。

bool変数と真偽値リテラルの比較に対する代案

ぼくならむしろtrue/falseとの比較ではなく、いっそのこと真偽値を反転した値を説明用変数に受けて、そいつにいい名前を付ける、と言う方法を採りますかね。

完全に人間にとっての可読性を考えて冗長的にするのであれば、説明用変数を使い、if文自体はシンプルに保つ。

同じ冗長手段ならこっちの方がオススメだと思ってます。

処理上不要な変数が増えるのは嫌だと思うかも知れませんが、普段からメソッドはシンプルな単位で小さく分割しているので、説明用変数のひとつふたつ増えたからと言って問題ありません。 というか、説明用変数を増やす余地すらないようなメソッドは、既に分割するべきレベルで大きいと考えます。

結論

是非に対する議論は面白いからそれはそれとして、おまいら、コーディング規約は守ろうな!!

あと、コーディング規約は陳腐化しないように、時流に合わせてきちんとメンテしような!!

*1:まぁ、実際にはTRUEでもFALSEでもない意味不明な2と言う値を食わせた奴が悪い、って話もあるんですけどね。そもそもその不適切な値がどういうルートで渡って来たのかは別途調べる必要があります。