私は、Scheme でのこれらの操作の違いは何なのだろうかと思います。Stack Overflow で同様の質問を見たことがありますが、それらは Lisp に関するものであり、これらの演算子の 3 つの間の比較はありません。
私はさまざまなタイプのコマンドをSchemeで書いており、次の出力が得られます。
(eq? 5 5) -->#t
(eq? 2.5 2.5) -->#f
(equal? 2.5 2.5) --> #t
(= 2.5 2.5) --> #t
これはなぜですか?
私は、Scheme でのこれらの操作の違いは何なのだろうかと思います。Stack Overflow で同様の質問を見たことがありますが、それらは Lisp に関するものであり、これらの演算子の 3 つの間の比較はありません。
私はさまざまなタイプのコマンドをSchemeで書いており、次の出力が得られます。
(eq? 5 5) -->#t
(eq? 2.5 2.5) -->#f
(equal? 2.5 2.5) --> #t
(= 2.5 2.5) --> #t
これはなぜですか?
eq?
#t
同じアドレス/オブジェクトの場合です。通常、同じシンボル、ブール値、およびオブジェクトに対して #t を期待し、異なる型、異なる値を持つ値、または同じ構造ではない値に対して #f を期待することができます。十分なスペースがある場合は、同じスペース内の値。R
したがって、 charや Fixnumのように、一部のポインターは実際にはアドレスではなく値です10
。これらはeq?
、「アドレス」が埋め込み型+値であるためです。一部の実装では、不変の定数も再利用されます。(eq? '(1 2 3) '(1 2 3)) は、解釈すると #f になる可能性がありますが、コンパイルすると #t になる可能性があります。これは、同じアドレスを取得する可能性があるためです。(Java の定数文字列プールのように)。このため、eq?
は指定されていないため、#t と評価されるか #f と評価されるかは実装に依存します。
eqv?
#t は と同じものeq?
です。データが大きすぎてポインターに収まらない場合でも、数値または文字であり、その値が同じ場合も #t です。したがって、それらeqv?
のタイプがサポートされているタイプのいずれかであり、両方が同じタイプであり、そのターゲット オブジェクトが同じデータ値を持っていることを確認する追加の作業が行われます。
equal?
は #t と同じものをeqv?
表し、それがペア、ベクトル、文字列、バイトベクトルなどの複合型の場合は、パーツを再帰的に処理しequal?
ます。実際には、2 つのオブジェクトが同じに見える場合は #t を返します。R6RS より前ではequal?
、円形構造で使用するのは安全ではありません。
=
のようですeqv?
が、数値型でのみ機能します。その方が効率的かもしれません。
string=?
に似equal?
ていますが、文字列に対してのみ機能します。その方が効率的かもしれません。
equal?
2 つのオブジェクト (任意の型) が等しいかどうかを再帰的に比較します。
リスト、文字列、ベクトルなど全体をトラバースする必要がある可能性があるため、大規模なデータ構造ではコストがかかる可能性があることに注意してください。
オブジェクトに要素が 1 つしか含まれていない場合 (例: 数字、文字など)、これは と同じeqv?
です。
eqv?
2 つのオブジェクトをテストして、両方が「通常は同じオブジェクトと見なされる」かどうかを判断します。
eqv?
とeq?
は非常によく似た操作であり、それらの違いは実装固有のものになります。eq?
と同じですが、eqv?
より細かい違いを識別できる場合があり、より効率的に実装される場合があります。
eqv?
。=
数値が等しいかどうかを比較します。
(= 1 1.0 1/1 2/2)
eq?
ポインターの等価性と考えてください。レポートの作成者は、可能な限り一般的なものにしたいと考えているため、これは実装に依存するため、はっきりとは言いません。そう言うと、ポインターベースの実装が有利になります。しかし、彼らは言います
通常は eq? を実装することができます。たとえば、単純なポインター比較として、eqv? よりもはるかに効率的です。
これが私の言いたいことです。(eqv? 2 2)
戻ること#t
が保証されてい(eq? 2 2)
ますが、指定されていません。次に、ポインターベースの実装を想像してください。それeq?
は単なるポインタ比較です。は指定されていないため(eq? 2 2)
、この実装では、ソース コードから読み取った新しい各数値の新しいメモリ オブジェクト表現を自由に作成できることを意味します。eqv?
実際にその引数を検査する必要があります。
OTOH(eq 'a 'a)
は#t
。これは、そのような実装では、重複した名前を持つシンボルを認識し、それらすべてに対してメモリ内の同じ1 つの表現オブジェクトを使用する必要があることを意味します。
実装がポインターベースではないとします。報告書に従っていれば問題ありません。作成者は、実装者に実装の詳細を指図していると見なされたくないので、言葉遣いを慎重に選択します。
とにかくこれは私の推測です。
非常に大まかに言えば、eq?
ポインターの等価性、eqv?
(アトミック) 値の認識、equal?
構造の認識 (引数を再帰的にチェックするため、最終的に(equal? '(a) '(a))
は である必要がある#t
)、=
数値の場合、string=?
文字列の場合、詳細は次のとおりです。レポートで。
以前の回答とは別に、いくつかコメントを追加します。
これらのすべての述語はidentity
、オブジェクトの抽象関数を定義しようとしていますが、コンテキストが異なります。
EQ?
are 2 objects the same?
実装に依存し、限られた用途でのみ質問に答えません。実装の観点から見ると、この述語は 2 つの数値 (オブジェクトへのポインター) を比較するだけで、オブジェクトの内容は調べません。したがって、たとえば、実装が文字列を内部に一意に保持せず、文字列ごとに異なるメモリを割り当てる場合、(eq? "a" "a")
false になります。
EQV?
-- これはオブジェクトの内部を調べますが、使用は制限されています。に対して true を返すかどうかは実装依存です(eqv? (lambda(x) x) (lambda(x) x))
。ここでは、この述語をどのように定義するかについての完全な哲学があります。これは、一部の関数の機能を比較するための高速な方法がいくつかあり、使用が限られていることが最近わかっているためです。ただしeqv?
、大きな数、文字列などに対して一貫した答えを提供します。
実際には、これらの述語の一部はオブジェクトの抽象的な定義を (数学的に) 使用しようとしますが、他の述語はオブジェクトの表現 (実際のマシンでの実装方法) を使用します。アイデンティティの数学的定義はライプニッツに由来し、次のように述べています。
X = Y iff for any P, P(X) = P(Y)
X, Y being objects and
P being any property associated with object X and Y.
理想的には、まさにこの定義をコンピューターに実装できるようにすることですが、決定不能性および/または速度の理由から、文字通りには実装されていません。これが、この定義に関するさまざまな視点に焦点を当てようとする多くのオペレーターが存在する理由です。
継続のためのアイデンティティの抽象的な定義を想像してみてください。関数のサブセット ( 関数のシグマ再帰クラス)の定義を提供できたとしても、言語は述語を true または false に強制しません。言語の定義と実装の両方が非常に複雑になります。
他の述語のコンテキストは、分析が容易です。