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

説明の一部書き直し
m (→‎論理的な等価: cl, 言い回し)
(説明の一部書き直し)
[[計算機科学]]において、'''関係演算子'''('''かんけいえんざんし''')とは、2つのエンティティの間の或る種の[[二項関係]]をテストする[[プログラミング言語]]の構成要素または[[演算子]]の種別で、2つの項の間の或る種の[[二項関係]]をテストする2項述語を指す。例として、数の[[同値関係|等値関係]](5 = 5 など)や[[不等式|不等関係]](4 ≥ 3 など)における "=", "≥" が挙げられる。[[Java]] などのように、[[型システム]]として[[ブーリアン型]]を独立に用意している言語では、関係演算子の返す値は、二つの[[オペランド]]の間で対応する関係が満たされているかどうかにって真か偽の値を返すになる。一方で、[[C言語]]などの言語では、関係演算子の返す値は整数 0 (偽を意味する) または 1 (真意味る) になる
 
関係演算子を用いて形成された[[式 (プログラミング)|式]]は、''関係式''または''条件''として知ら呼ばている。また、技術的な文献において、関係を言葉で説明する代わりに関係演算子が使用される事もある。関係演算子をサポートするプログラミング言語では、関係演算子は多くの場合[[中置記法]]によって記述される。すなわち、2つのオペランド(関係の対象となる式)の間に関係演算子が配置される。例えば、以下のC言語のコードは、'''x''' '''y''' より少ない場合にメッセージを表示する:物である。
<source lang="c">
if (x < y) {
}
</source>
また他方で、[[ポーランド記法]]を採用している言語もある。これらの言語では、関係演算子の後に続けて 2つのオペランドを並べる。例えば、[[Lisp]]では以下のように記述する:
<source lang="lisp">
(>= X Y)
| <code>&gt;</code>
| &gt;
| ''大なり (より大きい)''
|style="text-align: left;"| 左の値が右の値より大きいことをテストする。
|-
| <code>&lt;</code>
| &lt;
| ''小なり (より小さい)''
|style="text-align: left;"| 右の値が左の値より小さいことをテストする。
|-
:<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_3">註3: MATLAB は、殆どの演算子で C言語と同様のシンタックスを使用するが、<code>!=</code>を使用しない。MATLAB で <code>!</code>は、以降のテキストをコマンドラインとして[[オペレーティングシステム]]に送る働きを持つからである。</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>
==等式==
===代入演算子との混乱===
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言語の広範囲に及んだ普及のため、他の多くのプログラミング言語は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でなければ、以下のステートメントを実行せよ」''という意味になってしまう。(同じ演算子を持つ [[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>
 
<source lang="c">
</source>
 
言語やコンパイラの中には、この様なミスを事前に防ぐように工夫されている物がある。
Ada と [[Python]] においては、(<code>if</code>節も含めて)[[式 (プログラミング)|式]]の途中に代入演算子は登場できないので、この手の誤りは排除できる。[[GNUコンパイラコレクション]]などのいくつかのコンパイラでは、if の条件式中に代入演算子を含んでいるコード (意図的に書かれることもある) をコンパイルするときに警告を出す。
* 同じ演算子を持つ [[Java]] や [[C Sharp|C#]] も同じ問題を孕んでいるが、これらの言語ではこの誤りはコンパイルエラーとして検出できる。if 条件式はブーリアン型に制約を受け、また他の型(例えば整数型)からブーリアン型に暗黙的に変換されることもないからである。
Ada* と [[Python]] においては、(<code>if</code>節も含めて)[[式 (プログラミング)|式]]の途中に代入演算子は登場できないので、この手の誤りは排除できる。[[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>
 
==== 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つの[[文字列]]オブジェクトは別個のオブジェクトであるかもしれない(前者の意味では等しくない)が、同じ文字の並びを持ちうる(後者の意味で等しい)。
|}
<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 と同じように、比較演算子は、互いに他の比較演算子を用いて論理的に同値な命題を構成できる。これは、丁度[[ブール論理]]の[[論理演算]]子 XOR、AND、OR、NOT の間で見られる関係に似ている。以下の4つの条件式は互いに[[同値|論理的同値]]を持つ:である。
* <math>x < y</math>
* <math>y > x</math>
* <math>\neg (x \geq y)</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>
 
この性質をプログラミングに応用して、不等演算子 ≥ だけ (または、≥ と = の二つだけ) を真面目に実装し、他の比較演算子を ≥ (または、≥ と =) を用いて定義する事も行われる。
 
==脚注・出典==
60

回編集