関係演算子
計算機科学において、関係演算子(かんけいえんざんし)または比較演算子(ひかくえんざんし)とはプログラミング言語の構成要素または演算子の種別で、2つの項の間の或る種の二項関係をテストする2項述語を指す。例として、数の等値関係(5 = 5 など)や不等関係(4 ≥ 3 など)における "=", "≥" が挙げられる。Java などのように、型システムとしてブーリアン型を独立に用意している言語では、関係演算子の返す値は、二つのオペランドの間で対応する関係が満たされているかどうかによって真か偽になる。一方で、C言語などの言語では、関係演算子の返す値は整数 0 (偽を意味する) または 1 (真を意味する) になる。
関係演算子を用いて形成された式は、関係式または条件と呼ばれる。また、技術的な文献において、関係を言葉で説明する代わりに関係演算子が使用される事もある。関係演算子をサポートするプログラミング言語では、多くの場合中置記法によって記述される。すなわち、2つのオペランド(関係の対象となる式)の間に関係演算子が配置される。例えば、以下のC言語のコードは、x が y より少ない場合にメッセージを表示する物である。
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#、Go、Java、JavaScript、Perl(数値比較のみ)、PHP: Hypertext Preprocessor、Python、Ruby を含む。
- 註2: BASIC、Objective Caml、Pascal、SQL、Standard MLを含む。
- 註3: MATLAB は、殆どの演算子で C言語と同様のシンタックスを使用するが、
!=を使用しない。MATLAB で!は、以降のテキストをコマンドラインとしてオペレーティングシステムに送る働きを持つからである。 - 註4: Haskell を含む最初のフォーム。
- 註5: Bourne Shell、Korn Shell、Windows PowerShell を含む。シンボル
<と>はシェルの中では通常リダイレクトのために使用されるので、他の記号を用いる必要がある。頭のハイフンを除いた物は、Perl において文字列比較に使用される。
等式 [編集]
代入演算子との混乱 [編集]
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)"は大雑把に言えば「xとyが同値ならば以下のステートメントを実行せよ」を意味する。しかし、"if (x = y)" とミスタイプすると「xにyの値を割り当て、もしxの新しい値が0でなければ、以下のステートメントを実行せよ」という意味になってしまう。例えば、"if (x = y)" と書いてしまうと、yがxに代入され両方とも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); }
言語やコンパイラの中には、この様なミスを事前に防ぐように工夫されている物がある。
- 同じ演算子を持つ Java や C# も同じ問題を孕んでいるが、これらの言語ではこの誤りはコンパイルエラーとして検出できる。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では他の言語でも見られる様に、falseは0と等しい )の時に真を返す。これは、変数に 0 の値が割り当てられているかを確認するのに便利であるが、必ずしも期待される動作とは限らない[3]。一方で、"x === 0"は xが0の時のみ真を返す。
二つの等価性: オブジェクトの同一性と内容の同値性 [編集]
多くの現代的なプログラミング言語において、オブジェクトやデータ構造は参照を通じてアクセスされる。そのような言語では、2種類の異なる等価性を判定する必要性が生じる:
- 物理的な(浅い)等価 - 2つの参照が同じオブジェクトを参照するかどうか
- 構造的な(深い)等価 - 2つの参照によって参照されたオブジェクトがある意味において(例えば内容が同じである)等しいかどうか
通常、前者の等価性は後者の等価性を暗示しているが(自身に等しくないような NaN のようなものは除く)、逆は必ずしも真ではない。例えば、2つの文字列オブジェクトは別個のオブジェクトであるかもしれない(前者の意味では等しくない)が、同じ文字の並びを持ちうる(後者の意味で等しい)。
次の表では、これらの2種類の等価性を判定するための異なる方法を、様々な言語において一覧できるようにしてある。
| 言語 | 物理的な等価 | 構造的な等価 | 注意 |
|---|---|---|---|
| C言語, C++ | a == b |
*a == *b |
aとbはポインタである |
| C# | object.ReferenceEquals(a, b)1 |
a.Equals(b)1 |
|
| Common Lisp | (eq a b) |
(equal a b) |
|
| Java | a == b |
a.equals(b) |
aはb参照である |
| 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] |
aとbはオブジェクトへのポインタである |
1C# では、==演算子はデフォルトで ReferenceEquals になるが、代わりに Equals を実行する様に演算子オーバーロードをすることができる。この事によって、構造的な等価性の方がより直感的と思われる型において、== で構造的な等価性を判定する様にできる。特に文字列比較において、この事が効果的である (Java で文字列比較は a.equals(b) と書かなければならないが、C# では a==b と書ける)。
論理的同値性 [編集]
一見して自明ではないが、比較演算子は、互いに他の比較演算子を用いて論理的に同値な命題を構成できる。これは、丁度ブール論理の論理演算子 XOR、AND、OR、NOT の間で見られる関係に似ている。以下の4つの条件式は互いに論理的同値である。
更に、等値演算子も不等演算子を用いて表現する事が出来る。
この性質をプログラミングに応用して、不等演算子 ≥ だけ (または、≥ と = の二つだけ) を真面目に実装し、他の比較演算子を ≥ (または、≥ と =) を用いて定義する事も行われる。
関連項目 [編集]
脚注・出典 [編集]
- ^ Relational and Logical Operators of Mathematica
- ^ Kernighan, Brian; Dennis Ritchie (1988) [1978]. The C Programming Language (Second edition ed.). Prentice Hall., 19
- ^ a b “PHP: Comparison Operators - Manual”. 2008年7月31日閲覧。





