数学で考えると、次の2つの等式は同じ意味です。
(1) a = 2 (2) 2 = a
さて、C言語のプログラムで考えると、少し事情が変わります。もしこれらが、if や while の条件式の中に現れて、左右の項を比較しているのだとすればどうでしょう。C言語での比較は == と2文字で表記することに注意します。
(1') if (a == 2) { ... } (2') if (2 == a) { ... }
このように書けば、(1')(2') とも同じ意味になります。
しかし、もし代入を意味していれば、状況が異なります。代入の向きは右から左と決まっているので、以下の (1' ') は a に 2 を代入します。ところが (2' ') は文法エラーになります。2 に a の値を代入することはできないからです。
(1'') a = 2; // OK (2'') 2 = a; // NG
これは、代入される(できる)ものは特別なものである、ということでもあります。= の左側の式は、英語では "lvalue" という、普通の辞書には載っていない単語で表されます。日本語では「左辺値」との訳語があてられます。
左辺値は変数であればよいのかというと、そうでもありません。変数単独であればよいのですが、変数を用いた計算式には代入ができません。
a = 2; // OK a+1 = 3; // NG
同じように左辺値として x は大丈夫でも、x*x はエラーになります。もし x*x をある値にしたいのであれば、その逆関数である平方根を用いて x に代入するように式変形します。
(a) x*x = 4; // NG (b) x = sqrt(4); // OK (c) x = -sqrt(4); // OK(?)
(b)(c) のように、逆関数が1通りでない場合にどうすればよいのかは、プログラマが判断せねばなりません。
数学では「0≦x<10」のような表記をしますが、C言語の条件文としては、この表記では目的が達せられません。
(1) if ( 0 <= x < 10 ) { ... } // NG (2) if ( 0 <= x && x < 10 ) { ... } // OK (3) if ( x >= 0 && x < 10 ) { ... } // OK
(1) は、エラーにはなりませんが違う意味になってしまいます。正しくは(2)や(3)のように、2つの条件を && で結びます。一般に、2つの条件が同時に成りたって欲しければ &&、どちらか片方が成り立てばよいのであれば || をはさんで書き並べます。
(1) の表現には、gcc だと警告レベルを上げると検出してくれます(→Cygwinでデバッグ/不可解な動作)。Java ではエラーになります。
ちなみに (1) の式は ((0<=x) < 10) と評価されます。「(0<=x)」の比較結果は(違和感があると思いますが)0 か 1 の数値で表現されます(→比較演算)。それが 10 より小さいかが評価されるので、最終的には「常に成り立つ」ということになります。
(2) と (3) はどちらも同じ意味ですが、数直線を思い描く人はにとっては不等号の向きが揃った (2) の表現のほうが理解しやすいでしょう。変数が左にあるほうが落ち着く人には (3) が好まれるでしょう。
さて、上の条件の否定をどう書くのか考えてみます。いろいろなバリエーションが考えられます。
if ( 0 <= x && x < 10 ) { ... } // 否定する前 (a) if (!(0 <= x && x < 10) ) { ... } // 単純に ! をつけて否定にした (b) if (!(0 <= x) ||!(x < 10) ) { ... } // ! を分配して && と || を入れ替え (c) if ( 0 > x || x >= 10 ) { ... } // ! をやめて不等号を入れ替え (d) if ( x < 0 || x >= 10 ) { ... } // 左に変数が来るように (e) if ( x < 0 || 10 <= x ) { ... } // 不等号の向きを揃えた
どれも同じ意味ですが、よく使うのは (a), (d), (e) あたりでしょうか。
余談ですが、"<=" と "=<" のどちらか迷う人が多いですが、正しいのは片方だけです。覚え方は「+= も *= も <= も >= も、すべて = が後ろ」です。
C言語の2文字の演算子は = がいつでも後ろですが、Perl では残念なこと(?)に "=~" という演算子があります。
数学で考えれば a+b と a<b は計算の質が異なります。a+b はいつでも計算できます。反面 a<b は計算するものではなく、条件として扱うものです。
ところが、C言語をはじめとする多くのコンピュータ言語では、a+b と a<b のどちらも計算式であって、計算結果としての値を持ちます。C言語でも数学でも、a+b は当然のようにaとbの和です。そしてC言語では、a<b は成り立てば int 型の 1、成り立たなければ int 型の 0 の値になります。変数の a, b の型によらず、比較結果は必ず int 型になります。
1 とか 0 とかの値は、言語によって若干のバリエーションがあります。太古の BASIC インタプリタでは整数型の -1 と 0 でした。Java では boolean 型の true と false という値になります。
つまり、c = a+b とすれば c に a+b の値を代入できるように、c = a<b のような代入も可能です。従って、次のような書き換えもできてしまいます(が、あまり使いません)。
if ( a < b ) { ... } // 短縮(?)版
int cond = (a < b); // 分割版 if ( cond ) { ... }
次はよく使う例です。無限ループを書くのに、
while ( 0 == 0 ) { ... }
とする流儀がありますが、0==0 の演算結果は、成り立つので 1 となります。ですから、
while ( 1 ) { ... }
と表記するのと同じ意味になります。
条件式は 0 以外は成立とみなされるので、while (2) {...} でも無限ループです。
0 かどうかを判定する関数 is_zero() は次のような2通りの書き方が可能です。
int is_zero(int x) { if (x == 0) return 1; else return 0; }
int is_zero(int x) { // 短縮版 return (x == 0); }
上のような return の書き方は多用されているわけではありませんが、呼び出し側には注意が必要です。常套句があって、次のように書きます。これは頻繁に用います。
if ( is_zero(a) ) { ... }
if の条件式の中で比較を行っていないことに注目して下さい。比較は関数(短縮版)の中ですませているので、if の条件式では 1 と比較することすらしないのが流儀です。この流儀を用いるのはどういう場面かというと、関数名・変数名が is で始まっている時です。プログラムが英語の文章のように読めるでしょう、if a is zero, then ... のように。
否定の条件は次のように書きます。
if ( !is_zero(a) ) { ... }
これは if a is not zero, then ... のように理解します。
C言語の標準関数に isalpha() のようなものがあります。不成立なら 0 を返しますが、成立した場合に 1 ではなく、64 のような値を返すことがよくあります。ですから 1 と比較すると間違ったプログラムになってしまいます。
C 言語ではこのような関数・変数に int 型を用いるため、バラツキに対する注意が必要です。C++ や最新の C99 には bool 型があるのですが、やはり内部的な値にバラツキがあるので、同じ注意が必要です。安心なのは Java の boolean 型で、true と false の2通りの値に制限されています。