「関係演算子」の版間の差分

出典: フリー百科事典『ウィキペディア(Wikipedia)』
削除された内容 追加された内容
m →‎論理的な等価: cl, 言い回し
説明の一部書き直し
1行目: 1行目:
[[計算機科学]]において、'''関係演算子'''('''かんけいえんざんし''')とは、2つのエンティティの間の或る種の[[二項関係]]をテストする[[プログラミング言語]]の構成要素または[[演算子]]のを指す。例として、数の[[同値関係|等値関係]](5 = 5 など)や[[不等式|不等関係]](4 ≥ 3 など)が挙げられる。[[Java]] などのように、[[型システム]]として[[ブーリアン型]]を独立に用意している言語では、関係演算子は、二つの[[オペランド]]の間で対応する関係が満たされているかどうかにって真か偽の値を返す。一方で、[[C言語]]などの言語では、関係演算子は整数 0 または 1 をす。
[[計算機科学]]において、'''関係演算子'''('''かんけいえんざんし''')とは[[プログラミング言語]]の構成要素または[[演算子]]の種別で、2つの項の間の或る種の[[二項関係]]をテストする2項述語を指す。例として、数の[[同値関係|等値関係]](5 = 5 など)や[[不等式|不等関係]](4 ≥ 3 など)における "=", "≥" が挙げられる。[[Java]] などのように、[[型システム]]として[[ブーリアン型]]を独立に用意している言語では、関係演算子の返す値は、二つの[[オペランド]]の間で対応する関係が満たされているかどうかにって真か偽になる。一方で、[[C言語]]などの言語では、関係演算子の返す値は整数 0 (偽を意味する) または 1 (真意味る) になる


関係演算子を用いて形成された[[式 (プログラミング)|式]]は、''関係式''または''条件''として知らている。また、技術的な文献において、関係を言葉で説明する代わりに関係演算子が使用される事もある。関係演算子をサポートするプログラミング言語では、関係演算子は多くの場合[[中置記法]]によって記述される。すなわち、2つのオペランド(関係の対象となる式)の間に関係演算子がされる。例えば、以下のC言語のは、'''x'''が'''y'''より少ない場合にメッセージを表示する:
関係演算子を用いて形成された[[式 (プログラミング)|式]]は、''関係式''または''条件''と呼ばれる。また、技術的な文献において、関係を言葉で説明する代わりに関係演算子が使用される事もある。関係演算子をサポートするプログラミング言語では、多くの場合[[中置記法]]によって記述される。すなわち、2つのオペランド(関係の対象となる式)の間に関係演算子が配置される。例えば、以下のC言語のコードは、'''x''' '''y''' より少ない場合にメッセージを表示する物である。
<source lang="c">
<source lang="c">
if (x < y) {
if (x < y) {
7行目: 7行目:
}
}
</source>
</source>
また、[[ポーランド記法]]を採用している言語もある。例えば、[[Lisp]]では以下のように記述する:
他方で、[[ポーランド記法]]を採用している言語もある。これらの言語では、関係演算子の後に続けて 2つのオペランドを並べる。例えば、[[Lisp]]では以下のように記述する
<source lang="lisp">
<source lang="lisp">
(>= X Y)
(>= X Y)
62行目: 62行目:
| <code>&gt;</code>
| <code>&gt;</code>
| &gt;
| &gt;
| ''より大きい''
| ''大なり (より大きい)''
|style="text-align: left;"| 左の値が右の値より大きいことをテストする。
|style="text-align: left;"| 左の値が右の値より大きいことをテストする。
|-
|-
73行目: 73行目:
| <code>&lt;</code>
| <code>&lt;</code>
| &lt;
| &lt;
| ''より小さい''
| ''小なり (より小さい)''
|style="text-align: left;"| 右の値が左の値より小さいことをテストする。
|style="text-align: left;"| 右の値が左の値より小さいことをテストする。
|-
|-
100行目: 100行目:
:<cite id="fn_1">註1: [[C言語]]、[[C++]]、[[C Sharp|C#]]、[[Go (プログラミング言語)|Go]]、[[Java]]、[[JavaScript]]、[[Perl]](数値比較のみ)、[[PHP: Hypertext Preprocessor]]、[[Python]]、[[Ruby]] を含む。</cite>
:<cite id="fn_1">註1: [[C言語]]、[[C++]]、[[C Sharp|C#]]、[[Go (プログラミング言語)|Go]]、[[Java]]、[[JavaScript]]、[[Perl]](数値比較のみ)、[[PHP: Hypertext Preprocessor]]、[[Python]]、[[Ruby]] を含む。</cite>
:<cite id="fn_2">註2: [[BASIC]]、[[Objective Caml]]、[[Pascal]]、[[SQL]]、[[Standard ML]]を含む。</cite>
:<cite id="fn_2">註2: [[BASIC]]、[[Objective Caml]]、[[Pascal]]、[[SQL]]、[[Standard ML]]を含む。</cite>
:<cite id="fn_3">註3: MATLABは、殆どの演算子でC言語と同様のシンタックスを使用するが、<code>!=</code>を使用しない。MATLABで <code>!</code>は、以降のテキストをコマンドラインとして[[オペレーティングシステム]]に送る働きを持つからである。</cite>
:<cite id="fn_3">註3: MATLAB は、殆どの演算子で C言語と同様のシンタックスを使用するが、<code>!=</code>を使用しない。MATLAB で <code>!</code>は、以降のテキストをコマンドラインとして[[オペレーティングシステム]]に送る働きを持つからである。</cite>
:<cite id="fn_4">註4: [[Haskell]] を含む最初のフォーム。</cite>
:<cite id="fn_4">註4: [[Haskell]] を含む最初のフォーム。</cite>
:<cite id="fn_5">註5: [[Bourne Shell]]、[[Korn Shell]]、[[Windows PowerShell]] を含む。シンボル<code>&lt;</code>と<code>&gt;</code>はシェルの中では通常[[リダイレクト (CLI)|リダイレクト]]のために使用されるので、他の記号を用いる必要がある。頭のハイフンを除いた物は、[[Perl]] において文字列比較に使用される。</cite>
:<cite id="fn_5">註5: [[Bourne Shell]]、[[Korn Shell]]、[[Windows PowerShell]] を含む。シンボル<code>&lt;</code>と<code>&gt;</code>はシェルの中では通常[[リダイレクト (CLI)|リダイレクト]]のために使用されるので、他の記号を用いる必要がある。頭のハイフンを除いた物は、[[Perl]] において文字列比較に使用される。</cite>
106行目: 106行目:
==等式==
==等式==
===代入演算子との混乱===
===代入演算子との混乱===
C言語の広範囲に及んだ普及のた、他の多くのプログラミング言語はC言語の文法を参考にした。その典型的なものが値関係の関係演算子として標準的な "<code>=</code>" の代わりに "<code>==</code>" シンタックスを用いるという物である。この独特のシンタックスは、[[B言語]]開発の初期の段階で "<code>=</code>" を別の意味に割り当てたことに端を発する。([[ALGOL]]と[[FORTRAN]]に影響を受けた)B言語のデザイナーは、タイピングを減らしたいという要望から、頻繁に記述される値の更新・([[副作用 (プログラム)|副作用]]を伴う)[[変数 (プログラミング)#代入|代入]]操作のためのコピー演算子として、標準的な等値演算子 "<code>=</code>" を代用することを決定したのである。これは、<code>A=A+1</code> の様な見して「不可能」な式が許容されることを意味する。数十年後、C言語の人気のため、この用法は Java、C#、その他いくつかの言語でも採用された。他方、等値演算子 "<code>=</code>" の本来の意味を留めている言語としては [[Pascal]]、[[Object Pascal]]、[[Ada]]、[[Standard ML]]、[[Objective Caml]]、[[SQL]]、[[VHDL]] などがある。
C言語系統を初とする多くのプログラミング言語は、値関係の関係演算子として、直感的な "<code>=</code>" ではなく "<code>==</code>" を用いる。(一方、等値演算子 "<code>=</code>" の本来の意味を留めている言語としては [[Pascal]]、[[Object Pascal]]、[[Ada]]、[[Standard ML]]、[[Objective Caml]]、[[SQL]]、[[VHDL]] などがある。)


C言語の広範囲に及んだ普及のため、他の多くのプログラミング言語はC言語の文法を参考にしたが、その内の一つがこの ''"<code>==</code>" シンタックス''である。この独特のシンタックスは、[[B言語]]開発の初期の段階で "<code>=</code>" を別の意味に割り当てたことに端を発する。[[ALGOL]]と[[FORTRAN]]の流れを汲む B言語の設計者は、タイピングを減らしたいという要望から、頻繁に記述される更新・[[変数 (プログラミング)#代入|代入]]操作のためのコピー演算子として "<code>=</code>" を代用することを決定したのである (これは、<code>A=A+1</code> の様な一見して「不可能」な式が許容されることを意味する)。代わりに、"<code>=</code>" が本来担う役割である等値演算子として "<code>==</code>" が使われることとなった。C言語はこれらの演算子をそのまま引き継ぎ、以後、Java、C# を初めとする多くの言語がこのシンタックスを採用したのである。
これらのC言語系統における"<code>=</code>"の用法は[[バグ]]の温床になる。プログラマーが "<code>if (x == y)</code>" の代わりに、"<code>if (x = y)</code>" とミスタイプすることがあるのである。C言語において、"<code>if (x == y)</code>"は大雑把に言えば「''x''と''y''が[[同値]]ならば以下のステートメントを実行せよ」を意味する。しかし、"<code>if (x = y)</code>"とミスタイプすると「''x''に''y''の値を割り当て、もし''x''の新しい値が0でなければ、以下のステートメントを実行せよ」という意味になってしまう。(同じ演算子を持つ [[Java]]や[[C Sharp|C#]]も同じ問題を孕んでいるが、これらの言語ではこの誤りはコンパイルエラーとして検出できる。if 条件式はブーリアン型に制約を受け、また他の型(例えば整数型)からブーリアン型に暗黙的に変換されることもないからである。)例えば、"<code>if (x = y)</code>"と書いてしまうと、''y''が''x''に代入され両方とも2になり、更にxの値2は0ではないので、常にステートメントが実行される。従って、以下のコードは"<tt>''x'' is 2 and ''y'' is 2</tt>"を出力する。<ref name="kandr">{{cite book | title=The C Programming Language | last=Kernighan | first=Brian | coauthors=Dennis Ritchie | publisher=Prentice Hall | origyear=1978 | year=1988 | edition=Second edition}}, 19</ref>

これらのC言語系統における "<code>=</code>" の用法は[[バグ]]の温床になりうる。プログラマーが "<code>if (x == y)</code>" の代わりに、"<code>if (x = y)</code>" とミスタイプすることがあるのである。C言語において、"<code>if (x == y)</code>"は大雑把に言えば'''''x'''と'''y'''が[[同値]]ならば以下のステートメントを実行せよ」''を意味する。しかし、"<code>if (x = y)</code>" とミスタイプすると'''''x'''に'''y'''の値を割り当て、もし'''x'''の新しい値が0でなければ、以下のステートメントを実行せよ」''という意味になってしまう。例えば、"<code>if (x = y)</code>" と書いてしまうと、''y''が''x''に代入され両方とも2になり、更にxの値2は0ではないので、常にステートメントが実行される。従って、以下のコードは "<tt>x is 2 and y is 2</tt>" を出力する。<ref name="kandr">{{cite book | title=The C Programming Language | last=Kernighan | first=Brian | coauthors=Dennis Ritchie | publisher=Prentice Hall | origyear=1978 | year=1988 | edition=Second edition}}, 19</ref>


<source lang="c">
<source lang="c">
119行目: 121行目:
</source>
</source>


言語やコンパイラの中には、この様なミスを事前に防ぐように工夫されている物がある。
Ada と [[Python]] においては、(<code>if</code>節も含めて)[[式 (プログラミング)|式]]の途中に代入演算子は登場できないので、この手の誤りは排除できる。[[GNUコンパイラコレクション]]などのいくつかのコンパイラでは、if の条件式中に代入演算子を含んでいるコード (意図的に書かれることもある) をコンパイルするときに警告を出す。
* 同じ演算子を持つ [[Java]] や [[C Sharp|C#]] も同じ問題を孕んでいるが、これらの言語ではこの誤りはコンパイルエラーとして検出できる。if 条件式はブーリアン型に制約を受け、また他の型(例えば整数型)からブーリアン型に暗黙的に変換されることもないからである。
* [[GNUコンパイラコレクション]]などのいくつかのコンパイラでは、if の条件式中に代入演算子を含んでいるコード (意図的に書かれることもある) をコンパイルするときに警告を出す。
* Ada と [[Python]] においては、(<code>if</code>節も含めて)[[式 (プログラミング)|式]]の途中に代入演算子は登場できないので、この手の誤りは排除できる。
* 同様に、[[BASIC]] などのいくつかの言語では、文法的に弁別できることから、代入と等式の両方に "<code>=</code>" 記号を使用する(Ada や Python と同様に、代入演算子は式中に出現することがない)。


また、プログラマーの中には予防策として、定数に対する比較を記述する時、以下の様に直感とは逆の順でオペランドを記述する者もいる。この様にしておけば、誤って "<code>=</code>" と書くと 2 は左辺値ではないので、書いたコードは不正になる。コンパイラはこれに対してエラーメッセージを出すので、結果、適切な演算子に修正できるのである。このコーディングスタイルは left-hand comparison として知られている。
同様に、[[BASIC]] などのいくつかの言語では、文法的に弁別できることから、代入と等式の両方に "<code>=</code>" 記号を使用する(Ada や Python と同様に、代入演算子は式中に出現することがない)。

プログラマーの中には予防策として、定数に対する比較を記述する時、以下の様に直感とは逆の順でオペランドを記述する者もいる。この様にしておけば、誤って "<code>=</code>" と書くと 2 は左辺値ではないので、書いたコードは不正になる。コンパイラはこれに対してエラーメッセージを出すので、結果、適切な演算子に修正できるのである。このコーディングスタイルは left-hand comparison として知られている。


<source lang="c">
<source lang="c">
130行目: 134行目:
</source>
</source>


==== PHP での拡張 ====
===オブジェクトの同一性と内容の同値性===
[[PHP: Hypertext Preprocessor]]言語では、"<code>==</code>"シンタックスを更に拡張し、型が異なっても値が等しければ真を返す "<code>==</code>"演算子(例えば"<code>4 == "4"</code>"は真である)と、値が等しくかつ同じ型を持っている場合に真を返す "<code>===</code>"演算子(例えば "<code>4 === 4</code>" は真であるが "<code>4 === "4"</code>" は偽である)の2種類の演算子を持っている<ref name="php">{{cite web|url=http://php.net/manual/en/language.operators.comparison.php |title=PHP: Comparison Operators - Manual|accessdate=2008-07-31}}</ref>。"<code>x == 0</code>"はxが<code>0</code>、<code>"0"</code>(文字0を含む文字列)または <code>false</code>(PHPでは他の言語でも見られる様に、<code>false</code>は<code>0</code>と等しい )の時に真を返す。これは、変数に 0 の値が割り当てられているかを確認するのに便利であるが、必ずしも期待される動作とは限らない<ref name="php" />。一方で、"<code>x === 0</code>"は xが<code>0</code>の時のみ真を返す。
多くの現代的なプログラミング言語において、オブジェクトデータ構造は[[参照 (情報工学)|参照]]を通じてアクセスされる。そのような言語では、2種類の異なる等価性を判定する必要性が生じる:

*物理的な(浅い)等価 - 2つの参照が同じオブジェクトを参照するかどうか
===二つの等価性: オブジェクトの同一性と内容の同値性===
*構造的な(深い)等価 - 2つの参照によって参照されたオブジェクトがある意味において(例えば内容が同じである)等しいかどうか
多くの現代的なプログラミング言語において、オブジェクトデータ構造は[[参照 (情報工学)|参照]]を通じてアクセスされる。そのような言語では、2種類の異なる等価性を判定する必要性が生じる:
* 物理的な(浅い)等価 - 2つの参照が同じオブジェクトを参照するかどうか
* 構造的な(深い)等価 - 2つの参照によって参照されたオブジェクトがある意味において(例えば内容が同じである)等しいかどうか


通常、前者の等価性は後者の等価性を暗示しているが(自身に等しくないような [[NaN]] のようなものは除く)、逆は必ずしも真ではない。例えば、2つの[[文字列]]オブジェクトは別個のオブジェクトであるかもしれない(前者の意味では等しくない)が、同じ文字の並びを持ちうる(後者の意味で等しい)。
通常、前者の等価性は後者の等価性を暗示しているが(自身に等しくないような [[NaN]] のようなものは除く)、逆は必ずしも真ではない。例えば、2つの[[文字列]]オブジェクトは別個のオブジェクトであるかもしれない(前者の意味では等しくない)が、同じ文字の並びを持ちうる(後者の意味で等しい)。
168行目: 175行目:
|}
|}
<sup>1</sup>C# では、<code>==</code>演算子はデフォルトで <code>ReferenceEquals</code> になるが、代わりに <code>Equals</code> を実行する様に[[演算子オーバーロード]]をすることができる。この事によって、構造的な等価性の方がより直感的と思われる型において、<code>==</code> で構造的な等価性を判定する様にできる。特に[[文字列]]比較において、この事が効果的である (Java で文字列比較は <code>a.equals(b)</code> と書かなければならないが、C# では <code>a==b</code> と書ける)。
<sup>1</sup>C# では、<code>==</code>演算子はデフォルトで <code>ReferenceEquals</code> になるが、代わりに <code>Equals</code> を実行する様に[[演算子オーバーロード]]をすることができる。この事によって、構造的な等価性の方がより直感的と思われる型において、<code>==</code> で構造的な等価性を判定する様にできる。特に[[文字列]]比較において、この事が効果的である (Java で文字列比較は <code>a.equals(b)</code> と書かなければならないが、C# では <code>a==b</code> と書ける)。

===その他===
[[PHP: Hypertext Preprocessor]]言語では、このシンタックスを拡張し、型が異なっても値が等しければ真を返す "<code>==</code>"演算子(例えば"<code>4 == "4"</code>"は真である)と、値が等しくかつ同じ型を持っている場合に真を返す "<code>===</code>"演算子(例えば "<code>4 === 4</code>" は真であるが "<code>4 === "4"</code>" は偽である)の2種類の演算子を持っている<ref name="php">{{cite web|url=http://php.net/manual/en/language.operators.comparison.php |title=PHP: Comparison Operators - Manual|accessdate=2008-07-31}}</ref>。"<code>x == 0</code>"はxが<code>0</code>、<code>"0"</code>(文字0を含む文字列)または <code>false</code>(PHPでは他の言語でも見られる様に、<code>false</code>は<code>0</code>と等しい )の時に真を返す。これは、変数に 0 の値が割り当てられているかを確認するのに便利であるが、必ずしも期待される動作とは限らない<ref name="php" />。一方で、"<code>x === 0</code>"は xが<code>0</code>の時のみ真を返す。


==論理的同値性==
==論理的同値性==
一見して自明ではないが、[[ブール論理]]の[[論理演算]]子 XOR、AND、OR、NOT と同じように、比較演算子は、互いに他の比較演算子を用いて論理的に同値な命題を構成できる。以下の4つの条件式は互いに[[論理的同値]]を持つ:
一見して自明ではないが、比較演算子は、互いに他の比較演算子を用いて論理的に同値な命題を構成できる。これは、丁度[[ブール論理]]の[[論理演算]]子 XOR、AND、OR、NOT の間で見られる関係に似ている。以下の4つの条件式は互いに[[同値|論理的同値]]である。
* <math>x < y</math>
* <math>x < y</math>
* <math>y > x</math>
* <math>y > x</math>
* <math>\neg (x \geq y)</math>
* <math>\neg (x \geq y)</math>
* <math>\neg (y \leq x)</math>
* <math>\neg (y \leq x)</math>

更に、等値演算子も不等演算子を用いて表現する事が出来る。
* <math>x = y \Leftrightarrow \neg (x > y \vee y > x)</math>
* <math>x = y \Leftrightarrow x \geq y \wedge y \geq x</math>

この性質をプログラミングに応用して、不等演算子 ≥ だけ (または、≥ と = の二つだけ) を真面目に実装し、他の比較演算子を ≥ (または、≥ と =) を用いて定義する事も行われる。


==脚注・出典==
==脚注・出典==

2011年3月18日 (金) 14:51時点における版

計算機科学において、関係演算子かんけいえんざんし)とはプログラミング言語の構成要素または演算子の種別で、2つの項の間の或る種の二項関係をテストする2項述語を指す。例として、数の等値関係(5 = 5 など)や不等関係(4 ≥ 3 など)における "=", "≥" が挙げられる。Java などのように、型システムとしてブーリアン型を独立に用意している言語では、関係演算子の返す値は、二つのオペランドの間で対応する関係が満たされているかどうかによって真か偽になる。一方で、C言語などの言語では、関係演算子の返す値は整数 0 (偽を意味する) または 1 (真を意味する) になる。

関係演算子を用いて形成されたは、関係式または条件と呼ばれる。また、技術的な文献において、関係を言葉で説明する代わりに関係演算子が使用される事もある。関係演算子をサポートするプログラミング言語では、多くの場合中置記法によって記述される。すなわち、2つのオペランド(関係の対象となる式)の間に関係演算子が配置される。例えば、以下のC言語のコードは、xy より少ない場合にメッセージを表示する物である。

  if (x < y) {
      printf("x is less than y in this example\n");
  }

他方で、ポーランド記法を採用している言語もある。これらの言語では、関係演算子の後に続けて 2つのオペランドを並べる。例えば、Lispでは以下のように記述する。

(>= X Y)

標準的な関係演算子

プログラミング言語の中で使用される標準的な数値比較演算子を以下に示す。

標準的な関係演算子
プログラミング言語中で 印刷物中で 意味 用途
C言語ライク 1 BASICライク 2 Mathematica[1] MATLAB 3 FORTRAN 4 Bourneライクシェル 5 MUMPS
== = == Equal[x,y] == eq(x,y) == .EQ. -eq = = 等しい 2つの値が等しいことをテストする。
!= <> != Unequal[x,y] ~= ne(x,y) /= .NE. -ne '= 等しくない 2つの値が等しくないことをテストする。
> > > Greater[x,y] > gt(x,y) > .GT. -gt > > 大なり (より大きい) 左の値が右の値より大きいことをテストする。
< < < Less[x,y] < lt(x,y) < .LT. -lt < < 小なり (より小さい) 右の値が左の値より小さいことをテストする。
>= >= >= GreaterEqual[x,y] >= ge(x,y) >= .GE. -ge '< 以上 (大きいか等しい) 左の値が右の値より大きいか等しいことをテストする。
<= <= <= LessEqual[x,y] <= le(x,y) <= .LE. -le '> 以下 (小さいか等しい) 右の値が左の値より小さいか等しいことをテストする。
註1: C言語C++C#GoJavaJavaScriptPerl(数値比較のみ)、PHP: Hypertext PreprocessorPythonRuby を含む。
註2: BASICObjective CamlPascalSQLStandard MLを含む。
註3: MATLAB は、殆どの演算子で C言語と同様のシンタックスを使用するが、!=を使用しない。MATLAB で !は、以降のテキストをコマンドラインとしてオペレーティングシステムに送る働きを持つからである。
註4: Haskell を含む最初のフォーム。
註5: Bourne ShellKorn ShellWindows PowerShell を含む。シンボル<>はシェルの中では通常リダイレクトのために使用されるので、他の記号を用いる必要がある。頭のハイフンを除いた物は、Perl において文字列比較に使用される。

等式

代入演算子との混乱

C言語系統を初めとする多くのプログラミング言語では、同値関係の関係演算子として、直感的な "=" ではなく "==" を用いる。(一方、等値演算子 "=" の本来の意味を留めている言語としては PascalObject PascalAdaStandard MLObjective CamlSQLVHDL などがある。)

C言語の広範囲に及んだ普及のため、他の多くのプログラミング言語はC言語の文法を参考にしたが、その内の一つがこの "==" シンタックスである。この独特のシンタックスは、B言語開発の初期の段階で "=" を別の意味に割り当てたことに端を発する。ALGOLFORTRANの流れを汲む B言語の設計者は、タイピングを減らしたいという要望から、頻繁に記述される更新・代入操作のためのコピー演算子として "=" を代用することを決定したのである (これは、A=A+1 の様な一見して「不可能」な式が許容されることを意味する)。代わりに、"=" が本来担う役割である等値演算子として "==" が使われることとなった。C言語はこれらの演算子をそのまま引き継ぎ、以後、Java、C# を初めとする多くの言語がこのシンタックスを採用したのである。

これらのC言語系統における "=" の用法はバグの温床になりうる。プログラマーが "if (x == y)" の代わりに、"if (x = y)" とミスタイプすることがあるのである。C言語において、"if (x == y)"は大雑把に言えばxy同値ならば以下のステートメントを実行せよ」を意味する。しかし、"if (x = y)" とミスタイプするとxyの値を割り当て、もしxの新しい値が0でなければ、以下のステートメントを実行せよ」という意味になってしまう。例えば、"if (x = y)" と書いてしまうと、yxに代入され両方とも2になり、更にxの値2は0ではないので、常にステートメントが実行される。従って、以下のコードは "x is 2 and y is 2" を出力する。[2]

  int x = 1;
  int y = 2;
  if (x = y) {
      /* yが0でなければ以下のコードは常に実行される */
      printf("x is %d and y is %d\n", x, y);
  }

言語やコンパイラの中には、この様なミスを事前に防ぐように工夫されている物がある。

  • 同じ演算子を持つ JavaC# も同じ問題を孕んでいるが、これらの言語ではこの誤りはコンパイルエラーとして検出できる。if 条件式はブーリアン型に制約を受け、また他の型(例えば整数型)からブーリアン型に暗黙的に変換されることもないからである。
  • GNUコンパイラコレクションなどのいくつかのコンパイラでは、if の条件式中に代入演算子を含んでいるコード (意図的に書かれることもある) をコンパイルするときに警告を出す。
  • Ada と Python においては、(if節も含めて)の途中に代入演算子は登場できないので、この手の誤りは排除できる。
  • 同様に、BASIC などのいくつかの言語では、文法的に弁別できることから、代入と等式の両方に "=" 記号を使用する(Ada や Python と同様に、代入演算子は式中に出現することがない)。

また、プログラマーの中には予防策として、定数に対する比較を記述する時、以下の様に直感とは逆の順でオペランドを記述する者もいる。この様にしておけば、誤って "=" と書くと 2 は左辺値ではないので、書いたコードは不正になる。コンパイラはこれに対してエラーメッセージを出すので、結果、適切な演算子に修正できるのである。このコーディングスタイルは left-hand comparison として知られている。

  if (2 == a) {   /* =と==の誤用がコンパイルタイムエラーを引き起こす */
  }

PHP での拡張

PHP: Hypertext Preprocessor言語では、"=="シンタックスを更に拡張し、型が異なっても値が等しければ真を返す "=="演算子(例えば"4 == "4""は真である)と、値が等しくかつ同じ型を持っている場合に真を返す "==="演算子(例えば "4 === 4" は真であるが "4 === "4"" は偽である)の2種類の演算子を持っている[3]。"x == 0"はxが0"0"(文字0を含む文字列)または false(PHPでは他の言語でも見られる様に、false0と等しい )の時に真を返す。これは、変数に 0 の値が割り当てられているかを確認するのに便利であるが、必ずしも期待される動作とは限らない[3]。一方で、"x === 0"は xが0の時のみ真を返す。

二つの等価性: オブジェクトの同一性と内容の同値性

多くの現代的なプログラミング言語において、オブジェクトやデータ構造は参照を通じてアクセスされる。そのような言語では、2種類の異なる等価性を判定する必要性が生じる:

  • 物理的な(浅い)等価 - 2つの参照が同じオブジェクトを参照するかどうか
  • 構造的な(深い)等価 - 2つの参照によって参照されたオブジェクトがある意味において(例えば内容が同じである)等しいかどうか

通常、前者の等価性は後者の等価性を暗示しているが(自身に等しくないような NaN のようなものは除く)、逆は必ずしも真ではない。例えば、2つの文字列オブジェクトは別個のオブジェクトであるかもしれない(前者の意味では等しくない)が、同じ文字の並びを持ちうる(後者の意味で等しい)。

次の表では、これらの2種類の等価性を判定するための異なる方法を、様々な言語において一覧できるようにしてある。

言語 物理的な等価 構造的な等価 注意
C言語, C++ a == b *a == *b abはポインタである
C# object.ReferenceEquals(a, b)1 a.Equals(b)1
Common Lisp (eq a b) (equal a b)
Java a == b a.equals(b) ab参照である
Objective Caml a == b a = b
Pascal a^ = b^ a = b
Perl $a == $b $$a == $$b $a$bはスカラーへの参照である
PHP: Hypertext Preprocessor5 N/A $a == $b $a$bはオブジェクトである
Python a is b a == b
Ruby a.equal?(b) a == b
Scheme (eq? a b) (equal? a b)
Microsoft Visual Basic .NET a Is b a = b
Objective-C a == b [a isEqual:b] abはオブジェクトへのポインタである

1C# では、==演算子はデフォルトで ReferenceEquals になるが、代わりに Equals を実行する様に演算子オーバーロードをすることができる。この事によって、構造的な等価性の方がより直感的と思われる型において、== で構造的な等価性を判定する様にできる。特に文字列比較において、この事が効果的である (Java で文字列比較は a.equals(b) と書かなければならないが、C# では a==b と書ける)。

論理的同値性

一見して自明ではないが、比較演算子は、互いに他の比較演算子を用いて論理的に同値な命題を構成できる。これは、丁度ブール論理論理演算子 XOR、AND、OR、NOT の間で見られる関係に似ている。以下の4つの条件式は互いに論理的同値である。

更に、等値演算子も不等演算子を用いて表現する事が出来る。

この性質をプログラミングに応用して、不等演算子 ≥ だけ (または、≥ と = の二つだけ) を真面目に実装し、他の比較演算子を ≥ (または、≥ と =) を用いて定義する事も行われる。

脚注・出典

  1. ^ Relational and Logical Operators of Mathematica
  2. ^ Kernighan, Brian; Dennis Ritchie (1988) [1978]. The C Programming Language (Second edition ed.). Prentice Hall , 19
  3. ^ a b PHP: Comparison Operators - Manual”. 2008年7月31日閲覧。