コンピュータサイエンス において、関係演算子とは、 2つのエンティティ間の関係を構文的に定義するプログラミング言語の構成要素または演算子のことです。これには、数値の等価性(例:5 = 5)や不等式(例:4 ≥ 3)などが含まれます。
Pascal、Ada、Python 、 Javaなど、型システムに明確なブール型データ型を含むプログラミング言語では、これらの演算子は通常、2つのオペランド間の条件関係が成り立つかどうかに応じて、真または偽に評価されます。
C言語などの言語では、関係演算子は整数値0または1を返します。ここで、0は偽を表し、0以外の値はすべて真を表します。
関係演算子を用いて作成された式は、関係式または条件と呼ばれるものを形成する。関係演算子は、論理述語の特殊なケースと見なすことができる。
等価性は、多くのプログラミング言語の構成要素やデータ型で使用されます。要素が既にセット内に存在するかどうかをテストしたり、キーを介して値にアクセスしたりするために使用されます。また、switch文では制御フローを適切な分岐に振り分けるために使用され、論理プログラミングにおける単一化処理でも使用されます。
等価性には複数の有効な定義が存在し、特定の言語は設計上の様々な側面に応じて、それらの定義のうち1つ以上を採用する可能性がある。等価性の一つの意味として、「aがbと等しい場合、aとbはどのような文脈でも区別なく互換的に使用できる」というものがある。しかし、この記述は必ずしも成り立つとは限らず、特に内容の等価性に加えて可変性も考慮に入れると、その傾向は顕著になる。
特にオブジェクト指向プログラミングにおいては、比較によってデータ型と継承、等価性、同一性といった問題が生じる場合がある。多くの場合、以下の点を区別する必要がある。
多くの現代的なプログラミング言語では、オブジェクトやデータ構造は参照を介してアクセスされます。このような言語では、2種類の異なる等価性をテストする必要が生じます。
通常、最初の種類の等価性は2番目の等価性を意味します(ただし、非数(NaN)のようにそれ自体と等しくないものは除きます)。しかし、その逆は必ずしも真ではありません。たとえば、2つの文字列オブジェクトは、異なるオブジェクト(最初の意味で等しくない)であっても、同じ文字シーケンス(2番目の意味で等しい)を含む場合があります。この問題の詳細については、「同一性」を参照してください。
多くの単純な分数を含む実数は、浮動小数点演算で正確に表現することはできず、与えられた許容範囲内で等価性をテストする必要がある場合があります。しかし、このような許容範囲は、推移性などの望ましい性質を容易に損なう可能性があり、反射性も損なわれます。IEEE浮動小数点標準では、NaN ≠ NaNが成り立つことが要求されています。対照的に、(2022)ポジット演算のプライベート標準(ポジットの提唱者は IEEE floats を置き換えることを意図しています) には、NaR (Not a Real) という同様の概念があり、NaR = NaRが成り立ちます。[ 1 ]
計算可能な関数などの他のプログラミング要素は、等価性の概念を持たない場合や、等価性が計算不可能な場合があります。そのため、一部の言語では、基底クラス、インターフェース、トレイト、プロトコルなどの形で「比較可能」という概念を明示的に定義しており、これはソースコード内での宣言によって明示的に使用されるか、関連する型の構造を介して暗黙的に使用されます。
JavaScript、PHP、VBScript、およびその他のいくつかの動的型付け言語では、標準の等価演算子は、いわゆる緩い型付けに従います。つまり、 2 つの値が等しくなく、互換性のない型であっても、真と評価されますが、言語固有の規則によって互いに強制的に等しくすることができます。たとえば、数値 4 はテキスト文字列 "4" と等しく比較されます。このような動作は通常、言語を簡単にすることを目的としていますが、多くのプログラマーが知らない、驚くべき予測困難な結果につながる可能性があります。たとえば、JavaScript の緩い等価規則により、等価性が非推移的 (つまり、a == bおよびb == c、しかし) になったり、特定の値が自身の否定と等しくなったりすることがあります。[ 2 ]a != c
これらの言語では、厳密等価演算子もよく利用でき、同一または同等の型の値の場合にのみ真を返します(PHP では、4 === "4"は ですが、4 == "4"は真です)。[ 3 ] [ 4 ]数値 0 が偽と解釈される可能性のある言語では、この演算子を使用すると、ゼロのチェックなどの処理が簡略化されます(x == 0型に依存しない等価演算子を使用すると、x が 0 または "0" のいずれかである場合に真になります)。
非数値データに対する大小比較は、プログラミング言語に組み込まれている場合やプログラマーが設定可能なソート規則(テキスト文字列の場合は辞書順など)に従って実行されます。
2つのデータ項目(例えばaとb)の比較結果に数値を関連付けたい場合、通常はa < bの場合は-1、a = bの場合は0、a > bの場合は1を割り当てるという慣例が用いられます。例えば、C言語の関数は3方向比較をstrcmp実行し、この慣例に従って-1、0、または1を返します。また、qsort関数も比較関数がこの慣例に従って値を返すことを期待しています。ソートアルゴリズムにおいて、比較コードの効率はソートパフォーマンスに大きく影響する要素の一つであるため、非常に重要です。
プログラマ定義データ型(プログラミング言語に組み込まれていないデータ型)の比較は、カスタム関数やライブラリ関数(strcmp前述のものなど)によって行うこともできますし、一部の言語では比較演算子をオーバーロードすることで行うことも可能です。オーバーロードとは、比較対象のデータ型に応じてプログラマ定義の意味を割り当てることです。また、メンバーごとの比較といった慣例を用いることも別の方法です。
一見すると分かりにくいかもしれませんが、ブール論理演算子のXOR、AND、OR、NOTと同様に、関係演算子も論理的に等価になるように設計することができ、それらはすべて互いに定義することができます。次の4つの条件文は、任意のxとyの値に対して、すべて同じ論理的等価性E(すべて真またはすべて偽)を持ちます。
これは、ドメインがきちんと整理されていることを前提としている。
プログラミング言語でよく使われる数値関係演算子を以下に示します。標準SQL はBASIC と同じ演算子を使用しますが、多くのデータベースでは標準の演算子!=に加えて、さらに演算子も使用できます。SQL は厳密なブール代数に従います。つまり、以下のほとんどの言語で共通する短絡評価は使用しません。たとえばPHPには短絡評価がありますが、それ以外では、多くの SQL データベースと同様に、これら 2 つの演算子がエイリアスとして定義されています。<>
>=または使用)、強調のないプラットフォーム( を使用)、または大文字のみのプラットフォーム(または を使用) のコードで使用されます。GE≥bold'ge'.GE'GE'#<>!=!=その他の慣習はあまり一般的ではありません。Common LispとMacsyma / Maxima は、数値に対して Basic のような演算子を使用しますが、不等号は/=Common Lisp とMacsyma / Maxima で異なります。Common Lisp には、 、、、、 など#、さまざまな目的で使用される等価演算子と関係演算子のセットが複数あります。[ 6 ]古いLispでは、、 、 を使用し、残りの演算子に対して を使用してそれらを否定していました。eqeqlequalequalpstring=equalgreaterplesspnot
関係演算子は、技術文献では単語の代わりに使われることもあります。関係演算子は、プログラミング言語がサポートしていれば、通常は中置記法で記述されます。これは、演算子がオペランド(関連する2つの式)の間に現れることを意味します。たとえば、Pythonの式は、xがyより小さい場合に次のメッセージを出力します。
if x < y : print ( "この例では、x は y より小さいです" )Lispなどの他のプログラミング言語では、次のように接頭辞表記法が使用されます。
( >= X Y )数学では、3 < x < y < 20(3 < xかつx < yかつy < 20を意味する)のように、関係演算子を連鎖させるのが一般的です。数学におけるこれらの関係演算子は推移的であるため、構文は明確です。
しかし、最近の多くのプログラミング言語では、3 < x < y のような式は 2 つの左結合演算子 (または右結合演算子) から構成されているとみなされ、 のように解釈されます(3 < x) < y。 x=4 とすると、 となり(3 < 4) < y、評価すると となりますが、true < yこれは一般的に意味をなしません。ただし、C/C++ やその他の言語ではコンパイルされ、驚くべき結果 (ここでは、 が真である場合は数値 1 で表されるため) が得られます。
この式に馴染みのある数学的な意味を与えることは可能であり、Python やRakux < y < zなどのプログラミング言語ではそれが実現されています。C# や Java などの他の言語では、それが C ライクな言語における他のほとんどの中置演算子の動作と異なるため、そうしていません。Dプログラミング言語は、C との互換性を維持しているため、そのようなことはしていません。「C 式を許可しつつも、意味論が微妙に異なる(もっとも、正しい方向への変更ではあるが)とすれば、利便性よりも混乱を招くことになる」からです。[ 7 ]
Common Lispのような一部の言語では、このために複数の引数を持つ述語を使用します。Lisp では、(<= 1 x 10)x が 1 から 10 の間にある場合に真となります。
初期のFORTRAN(1956~57年)は文字セットが厳しく制限されており、関係演算子は のみでした。や は=なく(もちろん や もありませんでした)、そのため設計者は、、などの記号を定義せざるを得ず、数学的な用法との明らかな矛盾( は不可能であるはず)にもかかわらず、残りの文字をコピーに使用したくなる誘惑に駆られました。<>≤≥.GT..LT..GE..EQ.=X=X+1
国際代数言語 (IAL、ALGOL 58 ) およびALGOL (1958 年および 1960 年) は:=、代入のために導入され、等号には標準=が使用されるようになりました。この慣習は、CPL、ALGOL W、ALGOL 68、基本複合プログラミング言語 ( BCPL )、Simula、SET 言語 ( SETL )、Pascal、Smalltalk、Modula-2、Ada、Standard ML、OCaml、Eiffel、Object Pascal ( Delphi )、Oberon、Dylan、VHSIC ハードウェア記述言語 ( VHDL )、およびその他のいくつかの言語で採用されています。
ほとんどのプログラミング言語に共通するこの事実上の標準は、最終的にBというミニマルなコンパイル言語によって間接的に変更された。Bの唯一の目的は、(当時非常に原始的だった) Unixの最初の移植版として利用することだったが、その後、非常に影響力のあるC言語へと発展していった。
B は、 CPLの簡略化された (型なし) バージョンであるシステム プログラミング言語BCPLの構文変更版として始まりました。 「簡略化」プロセスとして説明されているように、BCPL [ 8 ]の演算子と演算子は、と(後にそれぞれとになる[ 9 ] )に置き換えられました。同じプロセスで、BCPL の ALGOL スタイルは B ではに置き換えられました。これらすべての理由は不明です[ 10 ]変数の更新には B で特別な構文 (や類似のものなど) がなく、式で許可されていたため、この等号の非標準的な意味は、等号の従来の意味を別の記号に関連付ける必要があることを意味しました。ケン トンプソンは、このためにアドホックな組み合わせを使用しました。andor&|&&||:==let==
後に小規模な型システムが導入されたため、BはCへと名称変更されました。この言語の人気とUnixとの関連性から、Java、C#、その他多くの言語が、等号の数学的な意味との不必要な矛盾にもかかわらず、構文的にこれに倣うようになりました。
C言語の代入には値があり、条件式ではゼロ以外のスカラー値は真と解釈されるため、[ 11 ]このコードは合法ですが、 とは全く異なる意味を持ちます。前者のコード断片は「yをxに代入し、 xの新しい値がゼロでない場合は、次のステートメントを実行する」という意味です。後者の断片は「xがyと等しい場合に限り、次のステートメントを実行する」という意味です。[ 12 ]if (x = y)if (x == y)
int x = 1 ; int y = 2 ; if ( x = y ) { // yが0以外の場合は必ずこのコードが実行されますprintf ( "xは%d、yは%d \n " , x , y ); }JavaとC#はCと同じ演算子を持っていますが、これらの言語では通常、この間違いはコンパイルエラーを引き起こします。なぜなら、if条件は型でなければならず、他の型(例えば数値)booleanから型に暗黙的に変換する方法がないからです。したがって、に代入される変数が型(またはラッパー型)でない限り、コンパイルエラーが発生します。booleanbooleanBoolean
Pascal、Delphi、AdaなどのALGOLライクな言語(関数定義のネストを許可するという意味で)、Python、その他多くの関数型言語などでは、代入演算子は式(句を含む)内に出現できないため、この種のエラーは発生しません。GNU Compiler Collectionif (GCC)などの一部のコンパイラは、if文内に代入演算子を含むコードをコンパイルする際に警告を発しますが、if条件内で代入を使用する正当な方法もいくつかあります。そのような場合は、警告を回避するために、代入を明示的に括弧で囲む必要があります。
同様に、 BASICなどの一部の言語では、代入と=等価性の両方に同じ記号を使用します。これは、それらが構文的に分離されているためです(Pascal、Ada、Pythonなどと同様に、代入演算子は式の中に使用できません)。
プログラマーの中には、定数との比較を通常とは逆の順序で記述する習慣を持つ人がいる。
// = と == の誤用はコンパイル時エラーになりますif ( 2 == a ) { // ... }が誤って使用されると=、2 は変数ではないため、結果として生成されるコードは無効になります。コンパイラはエラーメッセージを生成し、適切な演算子に置き換えることができます。このコーディングスタイルは、左辺比較、またはヨーダ条件と呼ばれます。
この表は、さまざまな言語におけるこれら2種類の等価性をテストするためのさまざまなメカニズムを示しています。
Rubyでは、a === b`b`は「bは集合aの要素である」という意味で使われますが、要素であることの意味の詳細は、関係するデータ型によって大きく異なります。`b`は、===ここでは「ケース等価性」または「ケース包含」演算子として知られています。