2013-07-10 9 views
7

An meiner Uni mussten wir mit Racket arbeiten und da es mir irgendwie gefiel, kaufte ich das kürzlich erschienene Buch "Realm Of Racket" von No Starch.Wie funktioniert eq? in Schlägerarbeit?

Es ist so weit, aber ich kann nicht herausfinden, was sie in Kapitel 4 bedeuten, wenn sie versuchen zu erklären, wie eq? Werke:

  1. Zuerst sie erklären, wie gleich? vergleicht, ob zwei Werte aus identischen Teilen bestehen. OK, kein Problem, ich habe das: gleich? funktioniert praktisch genauso wie Javas equals (someObject) Methode. Wenn zwei Objekte/Strukturen/was auch immer inhaltlich gleich sind, wird #t zurückgegeben.
  2. Dann dachte ich, eq? muss dem Java-Operator == entsprechen, der nicht inhaltlich, sondern auf Basis von Referenzen vergleicht.
  3. Dieser Gedanke schien durch den folgenden Satz in dem Buch bestätigt: "eq? Vergleicht, ob das Ändern einer Struktur ändert die andere Struktur ..." Großartig! Sagen wir es auf das folgende Java-Code vergleichen:

    Point p1 = new Point(5, 5); 
    Point p2 = p1; 
    System.out.println(p1 == p2); // true, since the reference has been copied. 
    System.out.println(p1.x);  // 5 
    System.out.println(p2.x);  // 5 
    p1.x = 42; 
    System.out.println(p1.x);  // 42 
    System.out.println(p2.x);  // Accordingly, 42 
    

    Lassen Sie uns versuchen, dies in Racket:

    (define cons1 (cons 1 empty)) 
    (define cons2 cons1) 
    (eq? cons1 cons2)   ;; #t, since the refernce has been copied. 
    (set! cons1 (cons 2 empty)) 
    cons1      ;; Returns '(2) - as expected. 
    cons2      ;; Still returns '(1). 
    

    Warum? cons2 verweist auf cons1, die selbst auf '(2) zeigt. Haben sie nicht auch gesagt, dass sie gleich sind, sobald man den anderen ändert?

Offensichtlich jetzt bekomme ich nicht, warum das nicht wie erwartet verhalten, und aus diesem Grunde, ich sehe nicht, was eq? geht. Ich bin vielleicht falsch, und es hat nichts mit Referenzen zu tun hat ...

Wenn jemand davon weiß, geben Sie bitte Ihre Weisheit teilen;)

Antwort

7

Eine technische Erklärung, wie eq? Werke zu sehen, die aktuelle specification, finden Sie keine detailliertere Referenz. Oder überprüfen Sie einfach Rackets documentation auf das Thema, insbesondere die Verfahren eq?, eqv? und equal?. In Bezug auf Ihre Frage - das Ergebnis ist wie erwartet und korrekt im Schema-Code, mal sehen warum. Beachten Sie, dass in dieser Zeile in Java:

p1.x = 42; 

Sie Modifizieren der gleichen Objekt, das an beiden von p1 und p2 hingewiesen hat wird. Während in dieser Zeile:

(set! cons1 (cons 2 empty)) 

Sie erstellen ein neue, anderes Objekt und cons1 Einstellung, um es zu zeigen, aber cons2 zeigt immer noch auf das alte Objekt. Sie können bestätigen, dass nach der vorherigen Zeile der Vergleich (eq? cons1 cons2)#f zurückgibt.

Der Punkt ist: die Beispiele sind nicht gleichwertig.Das Java-Beispiel behandelt ein einzelnes Objekt, auf das zwei verschiedene Referenzen verweisen, während das Schemabeispiel zwei Objekte und zwei Referenzen behandelt.

Zum Vergleich, hier ist ein Schema Beispiel, das mit dem Java-Code ähnlich ist, und arbeitet, wie Sie in hier, weil zu erwarten sind wir ein einzelnes veränderbares Objekt ändern, die auf zwei Referenzen hingewiesen haben werden:

#lang racket 
(require scheme/mpair) ;; `m` stands for "mutable" 

(define p1 (mlist 5 5)) 
(define p2 p1) 

(eq? p1 p2)  ;; #t 
(mcar p1)   ;; 5 
(mcar p2)   ;; 5 

(set-mcar! p1 42) 
(eq? p1 p2)  ;; #t 
(mcar p1)   ;; 42 
(mcar p2)   ;; 42 
2

Sie sind richtig, dass eq? Referenzen vergleicht, um Gleichheit zu bestimmen. Dies ist viel effizienter als equal?, die Objekte rekursiv vergleichen müssen.

Die Racket Dokumentation definiert ausdrücklich jede sowie die damit verbundene eqv? Funktion:

gleich?

Zwei Werte sind gleich? wenn und nur wenn sie eqv sind, sofern nicht anders für einen bestimmten Datentyp spezifiziert.

Datentypen mit weiterer Spezifikation von gleich? enthalten Strings, Byte-Strings, Paare, veränderbare Paare, Vektoren, Boxen, Hash-Tabellen und inspizierbare Strukturen. In den letzten sechs Fällen ist die Gleichheit rekursiv definiert; Wenn sowohl v1 als auch v2 Referenzzyklen enthalten, sind sie gleich, wenn die unendlichen Entfaltungen der Werte gleich wären.

Beispiele:

> (equal? 'yes 'yes) 
#t 
> (equal? 'yes 'no) 
#f 
> (equal? (expt 2 100) (expt 2 100)) 
#t 
> (equal? 2 2.0) 
#f 
> (equal? (make-string 3 #\z) (make-string 3 #\z)) 
#t 

äquiv?

Zwei Werte sind eqv? wenn und nur wenn sie eq sind, sofern nicht anders für einen bestimmten Datentyp spezifiziert.

Die Anzahl und Zeichen Datentypen sind die einzigen, für die eqv? unterscheidet sich von eq ?.

Beispiele:

> (eqv? 'yes 'yes) 
#t 
> (eqv? 'yes 'no) 
#f 
> (eqv? (expt 2 100) (expt 2 100)) 
#t 
> (eqv? 2 2.0) 
#f 
> (eqv? (integer->char 955) (integer->char 955)) 
#t 
> (eqv? (make-string 3 #\z) (make-string 3 #\z)) 
#f 

eq?

Geben Sie #t zurück, wenn sich v1 und v2 auf dasselbe Objekt beziehen, andernfalls #f.

Beispiele:

> (eq? 'yes 'yes) 
#t 
> (eq? 'yes 'no) 
#f 
> (let ([v (mcons 1 2)]) (eq? v v)) 
#t 
> (eq? (mcons 1 2) (mcons 1 2)) 
#f 
> (eq? (make-string 3 #\z) (make-string 3 #\z)) 
#f 
+0

Die 'eq?' Teile müssen sein, was 'eq?' Garantiert wird. Es gibt viel mehr, die in Wirklichkeit "eq?" Sind, aber wahrscheinlich nicht in der Zukunft garantiert werden, wie: '(eq? (+ 4 5) (+ 5 4)) ==> # t',' (eq? 5e100 5e100) ==> # t', '(eq?" Racketeer "" Racketeer ") ==> # t'. siehe http://docs.racket-lang.org/reference/eval-model.html#%28part._model-eq%29 – Sylwester

3

Ah: wollen Sie sicherstellen, dass Sie Struktur sind zu ändern. In Ihrem Beispiel wird die Struktur eines vorhandenen Werts nicht geändert, sondern stattdessen ein ganz neuer Wert erstellt und an diesen gerichtet. Sie haben das Konzept richtig: eq? ist so ziemlich Javas ==.

Gerade zu analogize, hier ist der Fehler in Java Form nur damit Sie sehen, was schief gelaufen ist:

int[] lst1 = new int[] { 1 };  // (define cons1 (cons 1 empty)) 
int[] lst2 = lst1;     // (define cons2 cons1) 
System.out.println(lst1 == lst2); // (eq? cons1 cons2) 
lst1 = new int[] { 2 };    // (set! cons1 (cons 2 empty)) 
System.out.println(lst1[0]);  // (list-ref cons1 0) 
System.out.println(lst2[0]);  // (list-ref cons2 0) 

In der Tat, an diesem Punkt wollen Sie für eq? -ness am Ende überprüfen von dies: Sie werden sehen, dass die beiden nicht mehr eq? sind: sie sind zwei unterschiedliche Werte.

Was Sie wirklich wollen, ist Mutation auf der Struktur zu tun, anstatt eine Variable neu zu binden. Da Listen in Racket unveränderlich sind, sollten Sie eine andere Datenstruktur verwenden, die eine Mutation ermöglicht. Die Vektoren sind ein Beispiel für einen Datentyp, der demonstriert werden kann. Lassen Sie uns "Rosetta" dies wieder so sehen Sie die Analogie:

(define vec1 (vector 1))   ;; int[] vec1 = new int[] { 1 }; 
(define vec2 vec1)    ;; int[] vec2 = vec1; 
(eq? vec1 vec2)     ;; System.out.println(vec1 == vec2); 
(vector-set! vec1 0 2)   ;; vec1[0] = 2; 
(vector-ref vec1 0)    ;; System.out.println(vec1[0]); 
(vector-ref vec2 0)    ;; System.out.println(vec2[0]);