第18回
条件式と演算子~制御構造をさらに理解する

簡潔な条件判断~三項目演算子

Cには、ifよりも簡潔に記述できる“?”という条件判断のための演算子があります。うまく使うとifよりも遙かに簡潔に、条件判断と分岐の構造を作れます。

if~elseを1行で

三項演算子“?”の書式は以下のようになります。
<式1> ? <式2> : <式3>;
<式1>が条件式で、これが真の場合は<式2>が、偽の場合は<式3>が実行されます。表計算ソフトのExcelで用いるIF関数の書式と同じです。

x >= 100 ? (y = 0) : (y = 100);
という式は
xが100以上ならyに0を代入
そうでなければ
yに100を代入
という動きをします。これはifを使って以下のように書くこともできます。
if (x >= 100) {
  y = 0;
} else {
  y = 100;
}
文を構成する式などの要素を「項」と言い、ifなどの構文は条件式という1つの項を必要としますが、“?”は3つの項を取るため三項演算子と呼ばれます。
図1:三項演算子の動作
図1:三項演算子の動作
図2:if命令を使った場合の動作
図2:if命令を使った場合の動作

複雑な条件には弱い

三項演算子“?”のメリットは、ifで書くと長くなる処理を簡潔に1行出かけるところです。上記のように処理自体は単純なのに何行にもわたってしまうif文は、三項演算子で書くとわかりやすくなります。

但し、複雑な条件判断と分岐の構造を三項演算子に当てはめると、かえってわかりにくくなることもあります。三項演算子では、各項に1つの式を記述する──という原則を守りましょう。複数の式も( )で囲めば1つの式にできますし、“?”もifのように入れ子(ネスト)構造を作れますが、元々簡潔な条件式の判定に用いることが目的なので、長いと処理の構造を読み取りにくくなってしまいます ※2

例えば、以下のようなif文があったとします。
if (x >= 100) {
  if (y < 1000) {
    z = 10;
  } else {
    z = 0;
  }
} else {
  z = 1000;
}
このif文は次のような意味を持っています。
xが100以上なら
  yが1000未満のときは
    zに10を代入
  そうでなければ
    zに0を代入
xが100未満なら
  zに1000を代入
これを三項演算子で書くと、以下のようになります。
(x >= 100) ? ((y < 1000) ? z = 10 : z = 0) : z = 1000;
図3:入れ子にした三項演算子の動作
図3:入れ子にした三項演算子の動作

入れ子にするとわかりにくい

上の式のアンダーラインの箇所が、入れ子構造の内側に位置します。この二重構造、確かに1行で記述できていますが、パッと見ただけでは意味不明ですね。以下のように行を分けて字下げすればわからないでもありませんが、先に掲げた何行にもわたるif文を読む方が遙かに楽です。
(x >= 100) ? 
  ((y < 1000) ?
     z = 10 : z = 0) :
   z = 1000;
また、三項演算子では、記述した式を判断する順序によって思わぬミスを招くこともあります。詳しくは回を追って説明しますが、三項演算子の入れ子はソースをわかりにくくすることが多いので避けた方が無難です。

なお、同じ“?”を使った三項演算子はJavaなどCから派生したプログラミング言語にも採用されています(言語によって若干仕様が異なっています)。

ExcelのIF関数は他に置き換えることができないので、何段階にも渡る条件判断の場合、( )を使って入れ子にするしかありません

あとがき

hiropの『ちょっと気になる専門用語』~《( )と空白》

関数名と( )の間

「専門用語」とはちょっと異なりますが、Cで多用される( )について取り上げます。

これまでに紹介してきたソースを見て、関数や命令語とその後ろの( )との間に半角のスペースがある場合とない場合の2通りあることに気付いた方もいると思います。

if(x >= 100) ...
と書いても
if (x >= 100) ...
  ↑空白
と書いてもエラーにはなりません。

しかし
c = getchar();

c = getchar ();
           ↑空白
と書くとコンパイル時エラーが発生します。

関数の後ろにはスペースを入れないで ( を記述します。引数があってもなくても、関数には( )が必須で、関数名と( )との間にスペースを入れてはいけません。これはCの規則です。

ifやreturenと( )の間

一方、if、for、while、returnなど関数ではない命令語では、続く( )との間にスペースを入れても入れなくても構いません。Cの規則ではどちらでも構わないことになっています。従って、次の2つの式はどちらもエラーとなりません。
if(x >= 100) ...
if (x >= 100) ...
  ↑空白

そこで、記述されたソースが関数の呼び出しなのか命令語なのかをわかりやすくするため、僕の場合は以下のような規則を適用しています。
1.関数の後ろにはスペースを入れない(これは文法規則)。
2.命令語の後ろにはスペースを入れる。

また、関数の値を返すreturn命令は、次のどの書き方をしても構いません。
return 0;
return(0);
return (0);
この場合も、returnで返す値をわかりやすくするために( )で囲み、さらにreturnが関数でないことを示すためにスペースを挿入することで、ソースが読みやすくなります。

これは規則ではなく、僕自身が勝手に決めた個人的なルールなので、皆さんが同じ書き方をする必要はありません。が、ソースをわかりやすくする――他者が見ても理解できるようにするためには、こういった工夫も役に立ちます。