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
なぜそうなのですか?
回答:
この質問に段階的に答えます。=
等価述語から始めましょう。=
述語は、二つの数が等しいかどうかをチェックするために使用されます。数字以外のものを指定すると、エラーが発生します。
(= 2 3) => #f
(= 2.5 2.5) => #t
(= '() '()) => error
eq?
述語は、その2つのパラメータは、メモリ内の同じオブジェクトをrespresentかどうかを確認するために使用されます。例えば:
(define x '(2 3))
(define y '(2 3))
(eq? x y) => #f
(define y x)
(eq? x y) => #t
ただし'()
、メモリには空のリストが1つしかないことに注意してください(実際には空のリストはメモリに存在しませんが、メモリ位置へのポインタ0
は空のリストと見なされます)。したがって、空のリストeq?
を比較すると、常に返されます#t
(メモリ内の同じオブジェクトを表すため)。
(define x '())
(define y '())
(eq? x y) => #t
実装によっては、数値や文字列などのプリミティブ値がeq?
返さ#t
れる場合と返されない場合があります。次に例を示します。
(eq? 2 2) => depends upon the implementation
(eq? "a" "a") => depends upon the implementation
ここでeqv?
述語が浮かび上がります。これeqv?
は、eq?
常に#t
同じプリミティブ値を返すことを除いて、述語とまったく同じです。例えば:
(eqv? 2 2) => #t
(eqv? "a" "a") => depends upon the implementation
したがってeqv?
、はのスーパーセットでeq?
あり、ほとんどの場合、のeqv?
代わりに使用する必要がありますeq?
。
最後に、equal?
述語に到達します。equal?
述語は、厳密に同じでありeqv?
、それは、2つのリストは、ベクターは、等が満たす要素対応しているかどうかを試験するために使用され得ることを除いて、述語eqv?
述語。例えば:
(define x '(2 3))
(define y '(2 3))
(equal? x y) => #t
(eqv? x y) => #f
一般に:
=
2つの数値が同等であるかどうかをテストする場合は、述語を使用します。eqv?
2つの非数値が同等であるかどうかをテストする場合は、述語を使用します。equal?
2つのリスト、ベクトルなどが同等であるかどうかをテストする場合は、述語を使用します。eq?
何をしているのかを正確に理解していない限り、述語を使用しないでください。(eqv? "a" "a") ==> unspecified
。equal?
または(おそらくより最適化された)を使用する必要がありますstring=?
(eq? 'foo 'foo) -> #t
、(eq? 'foo 'bar)
-> false`。私はこれをこことここで
に関連するRnRS仕様には完全な2ページがありますeq?, eqv?, equal? and =
。これがドラフトR7RS仕様です。見てみな!
説明:
=
数値を比較すると、2.5と2.5は数値的に等しい。equal?
数がに減少する=
場合、2.5と2.5は数値的に等しくなります。eq?
'ポインタ'を比較します。スキームの実装では、番号5は「即時」(可能性が高い)として実装されているため、5と5は同じです。数値2.5では、Schemeの実装で「浮動小数点レコード」の割り当てが必要になる場合があります。2つのポインターは同一ではありません。eq?
で#t
、それは同じアドレス/オブジェクトであるとき。通常、同じシンボル、ブール、オブジェクトには#t、異なる値、異なる値、または同じ構造ではない値には#fが期待できます。Scheme / Lisp-実装には、ポインタに型を埋め込み、埋め込むという伝統があります。十分なスペースがある場合は、同じスペースの値。したがって、charR
やFixnumのように、一部のポインタは実際にはアドレスではなく値です10
。これらはeq?
、「アドレス」が埋め込みタイプ+値であるためです。一部の実装では、不変の定数も再利用します。(eq? '(1 2 3)'(1 2 3))は、解釈されると#fになる可能性がありますが、同じアドレスを取得する可能性があるため、コンパイルされると#tになります。(Javaの定数文字列プールのように)。このため、多くの表現が含まれますeq?
は指定されていないため、#tと評価されるか#fと評価されるかは実装に依存します。
eqv?
と同じものの#tですeq?
。また、データが大きすぎてポインターに収まらない場合でも、数値または文字であり、値が同じである場合は#tです。したがって、それらについてeqv?
は、タイプがサポートされているものの1つであり、両方が同じタイプであり、そのターゲットオブジェクトが同じデータ値を持っていることを確認するという追加の作業を行います。
equal?
は#tであり、eqv?
ペア、ベクトル、文字列、バイトベクトルなどの複合型の場合はequal?
、パーツを再帰的に処理します。実際には、2つのオブジェクトが同じように見える場合は#tを返します。R6RSより前はequal?
、円形構造で使用するのは安全ではありません。
=
は似てeqv?
いますが、数値タイプでのみ機能します。それはより効率的かもしれません。
string=?
に似ていますがequal?
、文字列に対してのみ機能します。それはより効率的かもしれません。
equal?
(任意のタイプの)2つのオブジェクトが等しいかどうかを再帰的に比較します。
リスト、文字列、ベクトルなど全体をトラバースする必要がある可能性があるため、これは大規模なデータ構造ではコストがかかる可能性があることに注意してください。
オブジェクトに単一の要素(例:数字、文字など)が含まれている場合、これはと同じeqv?
です。
eqv?
2つのオブジェクトをテストして、両方が「通常は同じオブジェクトと見なされる」かどうかを判断します。
eqv?
とeq?
は非常によく似た操作であり、それらの違いは実装固有のものになります。eq?
と同じですが、eqv?
より細かい区別を識別できる可能性があり、より効率的に実装される可能性があります。
eqv?
。=
数値が等しいかどうか数値を比較します。
(= 1 1.0 1/1 2/2)
eq?
は実際のポインタの同等性だと思いました(ではありませんeqv?
)。それは「最高の、または最も識別力のある」ものです。たとえば、で(eqv? 2 2)
あることが保証されていますが#t
、(eq? 2 2)
「指定されていません」。つまり、実装が新しく読み取られた番号ごとに実際の新しいメモリオブジェクトを作成するか、可能であれば以前に作成されたものを再利用するかによって異なります。
eq?
とは、eqv?
他の操作よりも微妙です。
スキームの実装については言及していませんが、Racketではeq?
、引数が同じオブジェクトを参照している場合にのみtrueを返します。2番目の例は、システムが引数ごとに新しい浮動小数点数を作成しているため、#fを生成しています。それらは同じオブジェクトではありません。
equal?
と=
値の同等性をチェックしていますが=
、数値にのみ適用されます。
ラケットを使用している場合は、こちらで詳細を確認してください。それ以外の場合は、スキーム実装のドキュメントを確認してください。
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の述語を課しません。言語の定義と実装の両方が非常に複雑になります。
他の述語のコンテキストは分析が簡単です。
eqv?
から、何か別のことを意味している、eq?
またはequal?