関係演算子

出典: フリー百科事典『ウィキペディア(Wikipedia)』
移動: 案内検索

計算機科学において、関係演算子(かんけいえんざんし、relational operator)または比較演算子(ひかくえんざんし、comparison operator)とは、プログラミング言語演算子で、2つの対象の関係を調べるものをいう。たとえば、同値関係を調べる等号5 = 5=)や、順序関係を調べる不等号4 > 3>)などが含まれる。

Java など独立したブーリアン型型システムに持つ言語では、関係演算子は2つのオペランドの間に演算子が表す関係が成り立つかどうかによって真または偽を返す。一方で、C言語などの言語では、関係演算子は整数 0(偽を意味する)または 1(真を意味する)を返す。

関係演算子を含むは、関係式 (relational expression) または条件 (condition) と呼ばれる。また、技術的な文献において、関係を言葉で説明する代わりに関係演算子が用いられることもある。多くのプログラミング言語では、関係演算子は中置記法で記述される。たとえば、以下のC言語のコードは、xy より小さい場合にメッセージを表示するものである。

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

他方で前置記法を採用している言語もある。たとえば、Lispでは以下のように記述する。

(if (>= x y)
    (display "x is greater than or equal to y in this example"))

標準的な関係演算子[編集]

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

一般的な数値比較演算子
様式 等しい 等しくない より大きい より小さい 以上 以下
数学 = \ne > < \ge \le
Fortran[note 1] .EQ. .NE. .GT. .LT. .GE. .LE.
ALGOL 68[note 2] = > <
/= >= <=
eq ne gt lt ge le
BASICライク[note 3] = <> > < >= <=
MUMPS = '= > < '< '>
Pascalライク[note 4] = <> > < >= <=
C言語ライク[note 5] == != > < >= <=
shライクなシェル[note 6] -eq -ne -gt -lt -ge -le
バッチファイル EQU NEQ GTR LSS GEQ LEQ
MATLAB[note 7] == ~= > < >= <=
eq(x,y) ne(x,y) gt(x,y) lt(x,y) ge(x,y) le(x,y)
Mathematica[1] == != > < >= <=
Equal[x,y] Unequal[x,y] Greater[x,y] Less[x,y] GreaterEqual[x,y] LessEqual[x,y]
  1. ^ Fortran 90からはC言語ライクな比較演算子もサポートされている。
  2. ^ ALGOL 68: "stropping" regimes are used in code on platforms with limited character sets (e.g. use >= or GE instead of ), platforms with no bold emphasis (use 'ge'), or platforms with only UPPERCASE (use .GE or 'GE').
  3. ^ VB.NETOCamlSQLStandard MLなど。
  4. ^ SimulaModula-2DelphiAdaOberonOCamlStandard MLなど。
  5. ^ C、C++C#GoJavaJavaScriptPerl(文字列比較演算子は別に用意されている)、PHPPythonRubyRなど。
  6. ^ sh、bashkshWindows PowerShellなど。<> はシェルではリダイレクトの記号として用いられるため別の記号を用いる必要がある。先頭のハイフンを除いたものはPerlでは文字列比較演算子として使用される。
  7. ^ MATLABはC言語ライクな比較演算子を提供するが、!= を用いない。MATLABにおいて、! は以降のテキストをコマンド行としてオペレーティングシステムに送る働きをするからである。上段の形式はSmalltalkでも用いられるが、等号は = となる。

等号[編集]

代入演算子との混乱[編集]

C言語をはじめとする多くのプログラミング言語では、同値関係の関係演算子として、直感的な = ではなく == を用いる。(一方、= を用いる言語としては Pascal、Object Pascal、Ada、Standard ML、Objective Caml、SQL、VHDL などがある。)

C言語の普及は広範囲に及んだため、ほかの多くのプログラミング言語はC言語の文法を参考にしたが、そのうちの一つがこの == 演算子である。この独特の構文は、B言語開発の初期の段階で = を別の意味に割り当てたことに端を発する。ALGOLとFORTRANの流れを汲む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ではないので、常に if 文のブロックが実行される。したがって、以下のコードは "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);
}

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

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

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

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

PHP での拡張[編集]

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

オブジェクトの同一性と内容の等価性[編集]

多くの現代的なプログラミング言語において、オブジェクトやデータ構造は参照を通じてアクセスされる。そのような言語では、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は参照である
OCaml a == b a = b
Pascal a^ = b^ a = b
Perl $a == $b $$a == $$b $a$bはスカラーリファレンスである
PHP5 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)
VB.NET a Is b a = b
Objective-C a == b [a isEqual:b] abはオブジェクトへのポインタである
1 C# では、== 演算子はデフォルトで ReferenceEquals になるが、代わりに Equals を実行するように演算子オーバーロードをすることができる。このことによって、構造的な等価性の方がより直感的と思われる型において、== で構造的な等価性を判定するようにできる。特に文字列比較において、このことが効果的である(Java で文字列比較は a.equals(b) と書かなければならないが、C# では a == b と書ける)。

論理的同値性[編集]

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

  • x < y
  • y > x
  • \neg (x \geq y)
  • \neg (y \leq x)

さらに、等号も不等号を用いて表現することができる。

  • x = y \Leftrightarrow \neg (x > y \vee y > x)
  • x = y \Leftrightarrow x \geq y \wedge y \geq x

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

関連項目[編集]

出典[編集]

  1. ^ 関係演算子と論理演算子—Wolfram Mathematica 9 ドキュメント
  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日閲覧。