web-dev-qa-db-ja.com

Schemeのeq?、eqv?、equal ?、および=の違いは何ですか?

これらの操作の違いは何でしょうか。 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

なぜそうなのか、誰かが説明できますか?

71
yrazlik

この質問に少しずつ答えていきます。 _=_等価述語から始めましょう。 _=_述部は、2つの数値が等しいかどうかを確認するために使用されます。数値以外を指定すると、エラーが発生します。

_(= 2 3)     => #f
(= 2.5 2.5) => #t
(= '() '()) => error
_

_eq?_述語は、2つのパラメーターがメモリ内の同じオブジェクトを表すかどうかを確認するために使用されます。例えば:

_(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
_

一般に:

  1. 2つの数値が等しいかどうかをテストする場合は、_=_述語を使用します。
  2. 2つの非数値が同等かどうかをテストする場合は、_eqv?_述語を使用します。
  3. 2つのリスト、ベクトルなどが同等かどうかをテストする場合は、_equal?_述語を使用します。
  4. 正確に何をしているかわからない限り、_eq?_述語を使用しないでください。
132
Aadit M Shah

eq?, eqv?, equal? and =に関連するRNRS仕様には、完全な2ページがあります。 R7RS仕様案 です。見てみな!

説明:

  • =は数値を比較します。2.5と2.5は数値的に等しいです。
  • 数値のequal?=に減少し、2.5と2.5は数値的に等しくなります。
  • eq?は「ポインター」を比較します。あなたのScheme実装の5番は(おそらく)「即時」として実装されているため、5と5は同一です。数値2.5では、Scheme実装で「浮動小数点レコード」の割り当てが必要になる場合がありますが、2つのポインターは同一ではありません。
13
GoZoner

eq?は、同じアドレス/オブジェクトの場合、#tです。 通常、同じシンボル、ブール値、オブジェクトには#t、異なるタイプの値、異なる値、または同じ構造ではない値には#fを期待できますScheme/LISP実装には、ポインタに型を埋め込み、十分なスペースがある場合は同じスペースに値を埋め込むという伝統があります。したがって、char RやFixnum 10のように、一部のポインターは実際にはアドレスではなく値です。 「アドレス」は埋め込み型+値であるため、これらはeq?になります。一部の実装では、不変の定数も再利用します。 (eq? '(1 2 3)'(1 2 3))解釈されると#fになりますが、同じアドレスを取得する可能性があるため、コンパイルされると#tになります。 (Javaの定数文字列プールのように)。このため、eq?を含む多くの式は指定されていないため、#tまたは#fに評価されるかどうかは実装に依存します。

eqv?は、eq?と同じことの#tです。データが大きすぎてポインターに収まらない場合でも、数値または文字で値が同じの場合は#tです。したがって、これらのeqv?は、そのタイプがサポートされているものであり、両方が同じタイプであり、そのターゲットオブジェクトが同じデータ値を持っていることを確認する特別な作業を行います。

equal?eqv?と同じことを表す#tであり、ペア、ベクトル、文字列などの複合型の場合、バイトベクトルは、パーツでequal?を再帰的に行います。 実際には、2つのオブジェクトが同じように見える場合は#tを返します。 R6RSより前は、equal?を循環構造で使用することは安全ではありません。

=eqv?に似ていますが、数値型でのみ機能します。より効率的かもしれません。

string=?equal?に似ていますが、は文字列に対してのみ機能します。より効率的かもしれません。

8
Sylwester

スキームの実装については言及しませんが、ラケットでは、eq?は、引数が同じオブジェクトを参照する場合にのみtrueを返します。 2番目の例では、システムが各引数に対して新しい浮動小数点数を作成しているため、#fが生成されます。それらは同じオブジェクトではありません。

equal?および=は値の等価性をチェックしていますが、=は数字にのみ適用されます。

ラケットを使用している場合は、詳細について here を確認してください。それ以外の場合は、スキーム実装のドキュメントを確認してください。

5
Alan Gilbert

eq?をポインターの等価性と考えてください。 Report の作成者は、可能な限り一般的なものにしたいので、実装依存であり、ポインターベースの実装を好むため、このことをはっきりとは言いません。しかし、彼らは言う

通常、eqを実装することは可能ですか? eqv?よりもはるかに効率的です。たとえば、単純なポインター比較として

これが私が言っていることです。 (eqv? 2 2)#tを返すことが保証されていますが、(eq? 2 2)は指定されていません。ポインターベースの実装を想像してください。その中のeq?は単なるポインター比較です。 (eq? 2 2)が指定されていないため、この実装では、ソースコードから読み取る新しい数値ごとに新しいメモリオブジェクト表現を自由に作成できます。 eqv?は実際に引数を検査する必要があります。

OTOH (eq 'a 'a)#tです。つまり、このような実装では、名前が重複するシンボルを認識し、すべてのメモリに対して同じone表現オブジェクトを使用する必要があります。

実装がポインターベースではないと仮定します。レポートに準拠している限り、問題ではありません。作成者は、実装の詳細を実装者に指示するものとして見られたくないので、慎重に表現を選択します。

とにかくこれは私の推測です。

とても粗く、eq?はポインターの等価性、eqv?は(アトミック)値を認識し、equal?は構造を認識します(その引数を再帰的にチェックして、最終的に(equal? '(a) '(a))#tである必要があり、=は数字、string=?は文字列であり、詳細はレポートに記載されています。

3
Will Ness