Was ist der Unterschied zwischen Gl., Gl., Gleich? Und = im Schema?


85

Ich frage mich, was der Unterschied zwischen diesen Operationen in Schema ist. Ich habe ähnliche Fragen in Stack Overflow gesehen, aber es geht um Lisp, und es gibt keinen Vergleich zwischen drei dieser Operatoren.

Ich schreibe die verschiedenen Arten von Befehlen in Schema und erhalte die folgenden Ausgaben:

(eq? 5 5) -->#t
(eq? 2.5 2.5) -->#f
(equal? 2.5 2.5) --> #t
(= 2.5 2.5) --> #t

Warum ist das so?


3
und es gibt auch eqv?, was etwas anderes bedeutet als eq?oderequal?
newacct

Antworten:


155

Ich werde diese Frage schrittweise beantworten. Beginnen wir mit dem =Äquivalenzprädikat. Das =Prädikat wird verwendet, um zu überprüfen, ob zwei Zahlen gleich sind. Wenn Sie etwas anderes als eine Nummer angeben, wird ein Fehler ausgegeben:

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

Das eq?Prädikat wird verwendet, um zu überprüfen, ob seine beiden Parameter dasselbe Objekt im Speicher darstellen. Zum Beispiel:

(define x '(2 3))
(define y '(2 3))
(eq? x y)         => #f
(define y x)
(eq? x y)         => #t

Beachten Sie jedoch, dass sich nur eine leere Liste '()im Speicher befindet (tatsächlich existiert die leere Liste nicht im Speicher, aber ein Zeiger auf den Speicherort 0wird als leere Liste betrachtet). Daher werden beim Vergleich leerer Listen eq?immer zurückgegeben #t(da sie dasselbe Objekt im Speicher darstellen):

(define x '())
(define y '())
(eq? x y)      => #t

Abhängig von der Implementierung eq?kann nun #tfür primitive Werte wie Zahlen, Zeichenfolgen usw. zurückgegeben werden oder nicht . Zum Beispiel:

(eq? 2 2)     => depends upon the implementation
(eq? "a" "a") => depends upon the implementation

Hier kommt das eqv?Prädikat ins Spiel. Das eqv?ist genau das gleiche wie das eq?Prädikat, außer dass es immer #tfür dieselben primitiven Werte zurückgibt. Zum Beispiel:

(eqv? 2 2)     => #t
(eqv? "a" "a") => depends upon the implementation

Daher eqv?ist eine Obermenge von eq?und für die meisten Fälle sollten Sie eqv?anstelle von verwenden eq?.

Schließlich kommen wir zum equal?Prädikat. Das equal?Prädikat ist genau das gleiche wie das eqv?Prädikat, außer dass es auch verwendet werden kann, um zu testen, ob zwei Listen, Vektoren usw. entsprechende Elemente haben, die das eqv?Prädikat erfüllen . Zum Beispiel:

(define x '(2 3))
(define y '(2 3))
(equal? x y)      => #t
(eqv? x y)        => #f

Im Allgemeinen:

  1. Verwenden Sie das =Prädikat, wenn Sie testen möchten, ob zwei Zahlen gleichwertig sind.
  2. Verwenden Sie das eqv?Prädikat, wenn Sie testen möchten, ob zwei nicht numerische Werte äquivalent sind.
  3. Verwenden Sie das equal?Prädikat, wenn Sie testen möchten, ob zwei Listen, Vektoren usw. gleichwertig sind.
  4. Verwenden Sie das eq?Prädikat nur, wenn Sie genau wissen, was Sie tun.

7
AFAIK (eqv? "a" "a") ==> unspecified. Sie müssen verwenden equal?oder (die möglicherweise optimierte)string=?
Sylwester

3
nach dem Bericht , (eq? '(1) '(1))ist nicht spezifiziert , so dass Ihre (define x '(1 2))Darstellung funktionieren kann nicht.
Will Ness

4
Sehr genau und informativ. Besonders die Richtlinien am Ende.
Germán Diago

2
Aber Gl. scheint für Symbole definiert zu sein und dies sollte beachtet werden! Wenn die Symbole gleich aussehen, ist Gl. gibt #t zurück. Beispiel (eq? 'foo 'foo) -> #t, (eq? 'foo 'bar)-> false`. Ich habe das hier und hier
Nedko

13

Die RnRS-Spezifikation enthält zwei vollständige Seiten eq?, eqv?, equal? and =. Hier ist der Entwurf der R7RS-Spezifikation . Hör zu!

Erläuterung:

  • = vergleicht Zahlen, 2.5 und 2.5 sind numerisch gleich.
  • equal?für Zahlen reduziert sich auf =2.5 und 2.5 sind numerisch gleich.
  • eq?vergleicht 'Zeiger'. Die Nummer 5 in Ihrer Schema-Implementierung wird als "sofort" (wahrscheinlich) implementiert, daher sind 5 und 5 identisch. Die Zahl 2.5 erfordert möglicherweise die Zuweisung eines 'Gleitkomma-Datensatzes' in Ihrer Schema-Implementierung. Die beiden Zeiger sind nicht identisch.

1
Der Link zum Entwurf der R7RS-Spezifikation ist seit dem 04.02.2018
Jeremiah Peschka

2
Auf einen Live-Link aktualisiert.
GoZoner

10

eq?ist, #twenn es die gleiche Adresse / das gleiche Objekt ist. Normalerweise kann man erwarten, dass #t für dasselbe Symbol, denselben Booleschen Wert und dasselbe Objekt und #f für Werte unterschiedlichen Typs, mit unterschiedlichen Werten oder nicht derselben Struktur verwendet wird. Schema- / Lisp-Implementierungen haben die Tradition, Typen in ihre Zeiger einzubetten und einzubetten Werte im selben Raum, wenn genügend Platz vorhanden ist. Daher sind einige Zeiger wirklich keine Adressen, sondern Werte wie Rdas Zeichen oder das Fixnum 10. Dies eq?geschieht , da die "Adresse" ein eingebetteter Typ + Wert ist. Einige Implementierungen verwenden auch unveränderliche Konstanten wieder. (eq? '(1 2 3)' (1 2 3)) könnte bei der Interpretation #f sein, beim Kompilieren jedoch #t, da es möglicherweise dieselbe Adresse erhält. (Wie der konstante String-Pool in Java). Aus diesem Grund sind viele Ausdrücke involvierteq? sind nicht spezifiziert, daher ist es implementierungsabhängig, ob es als #t oder #f ausgewertet wird.

eqv?sind #t für die gleichen Dinge wie eq?. Es ist auch #t, wenn es sich um eine Zahl oder ein Zeichen handelt und der Wert gleich ist , selbst wenn die Daten zu groß sind, um in einen Zeiger zu passen. Für diese eqv?bedeutet die zusätzliche Arbeit, zu überprüfen, ob dieser Typ einer der unterstützten ist, dass beide vom selben Typ sind und die Zielobjekte den gleichen Datenwert haben.

equal?ist #t für die gleichen Dinge wie eqv?und wenn es sich um einen zusammengesetzten Typ wie Paar, Vektor, Zeichenfolge und Bytevektor handelt, wird dies rekursiv equal?mit den Teilen durchgeführt. In der Praxis wird #t zurückgegeben, wenn die beiden Objekte gleich aussehen . Vor R6RS ist die Verwendung equal?in kreisförmigen Strukturen nicht sicher .

=ist wie, eqv?aber es funktioniert nur für numerische Typen . Es könnte effizienter sein.

string=?ist wie equal?, aber es funktioniert nur für Strings. Es könnte effizienter sein.


6

equal? vergleicht rekursiv zwei Objekte (eines beliebigen Typs) auf Gleichheit.

  • Beachten Sie, dass dies für eine große Datenstruktur teuer sein kann, da möglicherweise die gesamte Liste, Zeichenfolge, der Vektor usw. durchlaufen werden muss.

  • Wenn das Objekt nur ein einzelnes Element enthält (z eqv?. B. Nummer, Zeichen usw.), ist dies dasselbe wie .


eqv? testet zwei Objekte, um festzustellen, ob beide "normalerweise als dasselbe Objekt angesehen werden".

  • eqv?und eq?sind sehr ähnliche Operationen, und die Unterschiede zwischen ihnen werden etwas implementierungsspezifisch sein.

eq?ist das Gleiche wie eqv?, kann jedoch feinere Unterscheidungen erkennen und kann effizienter implementiert werden.

  • Gemäß der Spezifikation könnte dies als schneller und effizienter Zeigervergleich implementiert werden, im Gegensatz zu einer komplizierteren Operation für eqv?.


= vergleicht Zahlen auf numerische Gleichheit.

  • Beachten Sie, dass mehr als zwei Nummern angegeben werden können, z. (= 1 1.0 1/1 2/2)

Ich dachte, eq?es wäre tatsächliche Zeigergleichheit (nicht eqv?). Es ist "das Beste oder Diskriminierendste". ZB (eqv? 2 2)ist garantiert #t, aber (eq? 2 2)"nicht spezifiziert". Das heißt, es hängt davon ab, ob eine Implementierung für jede neu gelesene Nummer ein tatsächlich neues Speicherobjekt erstellt oder ein zuvor erstelltes Objekt wiederverwendet, wenn dies möglich ist.
Will Ness

@ WillNess - Guter Fang, danke. Die Unterschiede zwischen eq?und eqv?sind subtiler als die anderen Operationen.
Justin Ethier

5

Sie erwähnen keine Schemaimplementierung, geben jedoch in Racket eq?nur true zurück, wenn sich die Argumente auf dasselbe Objekt beziehen. Ihr zweites Beispiel liefert #f, da das System für jedes Argument eine neue Gleitkommazahl erstellt. Sie sind nicht dasselbe Objekt.

equal?und =prüfen auf Wertäquivalenz, gelten jedoch =nur für Zahlen.

Wenn Sie Racket verwenden, finden Sie hier weitere Informationen. Überprüfen Sie andernfalls die Dokumentation Ihrer Schemaimplementierung.


3
Besser noch ... Lesen Sie die Spezifikation ... r6rs.org/final/html/r6rs/r6rs-ZH-14.html#node_sec_11.5
Dirk

3

Stellen Sie sich eq?Zeigergleichheit vor. Die Autoren des Berichts möchten, dass er so allgemein wie möglich ist, damit sie dies nicht direkt sagen, da er implementierungsabhängig ist und die zeigerbasierten Implementierungen bevorzugen würde. Aber sie sagen

Es wird normalerweise möglich sein, Gl. viel effizienter als eqv?, zum Beispiel als einfacher Zeigervergleich

Hier ist was ich meine. (eqv? 2 2)wird garantiert zurückkehren #t, (eq? 2 2)ist aber nicht spezifiziert. Stellen Sie sich nun eine zeigerbasierte Implementierung vor. Darin eq?ist nur ein Zeigervergleich. Da dies (eq? 2 2)nicht angegeben ist, bedeutet dies, dass diese Implementierung nur eine neue Speicherobjektdarstellung für jede neue Nummer erstellen kann, die sie aus dem Quellcode liest. eqv?muss tatsächlich seine Argumente überprüfen.

OTOH (eq 'a 'a)ist #t. Dies bedeutet , dass ein solche Umsetzung Symbole mit doppelten Namen erkennen muß und das gleiche verwendet eine Darstellung Objekt im Speicher für alle von ihnen.

Angenommen, eine Implementierung basiert nicht auf Zeigern. Solange es sich an den Bericht hält, spielt es keine Rolle. Die Autoren möchten einfach nicht so gesehen werden, dass sie den Implementierern die Besonderheiten der Implementierungen diktieren, deshalb wählen sie ihren Wortlaut sorgfältig aus.

Das ist sowieso meine Vermutung.

So sehr grob, eq?ist eqv?Zeigergleichheit , ist (atomar-) wertebewusst, equal?ist auch strukturbewusst (prüft seine Argumente rekursiv, so dass es schließlich (equal? '(a) '(a))erforderlich ist #t), =ist für Zahlen, string=?ist für Zeichenfolgen und die Details sind im Bericht.


0

Abgesehen von den vorherigen Antworten werde ich einige Kommentare hinzufügen.

Alle diese Prädikate möchten die abstrakte Funktion identityeines Objekts definieren, jedoch in unterschiedlichen Kontexten.

EQ?ist implementierungsabhängig und beantwortet die Frage nicht are 2 objects the same?nur in begrenztem Umfang. Aus Sicht der Implementierung vergleicht dieses Prädikat nur 2 Zahlen (Zeiger auf Objekte), es betrachtet nicht den Inhalt der Objekte. Wenn Ihre Implementierung beispielsweise die Zeichenfolgen nicht eindeutig enthält, sondern für jede Zeichenfolge einen anderen Speicher reserviert, ist (eq? "a" "a")dies falsch.

EQV?- Dies sieht in die Objekte, aber mit begrenzter Verwendung. Es ist implementierungsabhängig, wenn es true für zurückgibt (eqv? (lambda(x) x) (lambda(x) x)). Hier ist eine vollständige Philosophie, wie dieses Prädikat definiert wird, da wir heutzutage wissen, dass es einige schnelle Methoden gibt, um die Funktionalität einiger Funktionen mit begrenzter Verwendung zu vergleichen. eqv?Bietet aber eine kohärente Antwort für große Zahlen, Zeichenfolgen usw.

In der Praxis versuchen einige dieser Prädikate, die abstrakte Definition eines Objekts (mathematisch) zu verwenden, während andere die Darstellung eines Objekts verwenden (wie es auf einer realen Maschine implementiert ist). Die mathematische Definition von Identität stammt von Leibniz und lautet:

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.

Idealerweise wäre es möglich, genau diese Definition auf einem Computer zu implementieren, aber aus Gründen der Unentschlossenheit und / oder Geschwindigkeit wird sie nicht wörtlich implementiert. Aus diesem Grund gibt es viele Operatoren, die versuchen, sich auf unterschiedliche Gesichtspunkte rund um diese Definition zu konzentrieren.

Versuchen Sie sich die abstrakte Definition einer Identität für eine Fortsetzung vorzustellen. Selbst wenn Sie eine Definition einer Teilmenge von Funktionen ( Sigma-rekursive Funktionsklasse ) bereitstellen können, legt die Sprache kein Prädikat fest, das wahr oder falsch ist. Dies würde sowohl die Definition der Sprache als auch die Implementierung erheblich erschweren.

Der Kontext für die anderen Prädikate ist einfacher zu analysieren.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.