2009-04-21 4 views
1

Ich bemerkte dieses halb seltsame Verhalten in einem meiner Projekte mit Scheme und Listen. Ich habe es geschafft, das Verhalten in einen einzigen Abschnitt zu isolieren. Der Code ist:Auto-Rückgabewert? Oder Schema ist seltsam?

(define x (list 1 2 3)) 
(define y (list 4 5)) 
(define z (cons (car x) (cdr y))) 
(define w (append y z)) 
(define v (cons (cdr x) (cdr y))) 
(set-car! x 6) 
(set-car! y 7) 
(set-cdr! (cdr x) (list 8)) 

x 
y 
z 
w 
v 

gibt uns die Ausgabe von:

(6 2 8) 
(7 5) 
(1 5) 
(4 5 1 5) 
((2 8) 5) 

jemand mir erklären kann:

  1. Warum (set-car! x 6) nicht Z aktualisieren? Da nach meinem Verständnis car/cdr Rückgabe Zeiger oder Verweise auf die entsprechenden Werte. Das ist wirklich komisch und ich bin irgendwie verwirrt.
  2. Wenn car/cdr keine Referenzen/Zeiger zurückgibt, wie ist dann die letzte set-cdr! Manipulation der Liste v?

Irgendwelche Ideen? Es ist eine einfache Lösung, aber ich bin neugieriger, warum die Seltsamkeit mit den Variablen weitergeht.

Antwort

21

Okay, gehen wir Schritt für Schritt durch Ihr Programm. Ich vergebe auch eindeutige Nummern (denke an sie als Objektadressen, wenn du an C-ähnliche Sprachen gewöhnt bist) für jedes neu erstellte Objekt, damit du sehen kannst, was was ist. :-)

(define x (list 1 2 3))    ; => #1 = (1 . #2), #2 = (2 . #3), #3 = (3 .()) 
(define y (list 4 5))    ; => #4 = (4 . #5), #5 = (5 .()) 
(define z (cons (car x) (cdr y))) ; => #6 = (1 . #5) 
(define w (append y z))    ; => #7 = (4 . #8), #8 = (5 . #6) 
(define v (cons (cdr x) (cdr y))) ; => #9 = (#2 . #5) 
(set-car! x 6)      ; => #1 = (6 . #2) 
(set-car! y 7)      ; => #4 = (7 . #5) 
(set-cdr! (cdr x) (list 8))   ; => #2 = (2 . #10), #10 = (8 .()) 

Nun lassen Sie uns auf Ihre Werte aussehen (für jede Referenz, die zuletzt zugewiesene Wert verwenden):

x ; #1 => (6 . #2) => (6 . (2 . #10)) => (6 2 8) 
y ; #4 => (7 . #5) => (7 5) 
z ; #6 => (1 . #5) => (1 5) 
w ; #7 => (4 . #8) => (4 . (5 . #6)) => (4 . (5 . (1 . #5))) => (4 5 1 5) 
v ; #9 => (#2 . #5) => ((2 . #10) 5) => ((2 8) 5) 

Edit: Ich bin ein Diagramm Hinzufügen meine erklären Antwort, da Sie keine Diagramme in einem Kommentar haben können. Ich habe keine Zeit, ein Diagramm zu erstellen, das die obigen Werte zeigt, aber das erklärt hoffentlich ein paar Dinge.

Expression tree

Jedes Paar hat zwei "Slots", car und cdr, wie die linke und rechte Kästen in dem Diagramm oben dargestellt. Jeder dieser Schlitze, wie Sie sehen, hat drei mögliche Dinge:

  1. ein Atom (eine Zahl in Ihren Beispielen oder Symbole in dem Diagramm, wie let, s5 und sqrt)
  2. Ein Verweis (im Diagramm)
  3. Null (dargestellt als Blackbox in dem Diagramm als Pfeil dargestellt)

Sie irgendwelche dieser in einem der Slots setzen können. Also, in meiner obigen Erklärung, jedes der # Elemente ist ein Pfeil, jede der # Zahlen ist ein Atom, und jeder der () ist eine Black Box.Also, in der Linie

(define v (cons (cdr x) (cdr y))) 

Sie ein Paar erstellen, wo der linke Schlitz hat den gleichen Inhalt wie der rechten Schlitz x (dh ein Pfeil nach 2 paaren) und dem Recht -Hand-Slot hat den gleichen Inhalt wie der rechte Slot von y (ein Pfeil geht zu Paar 5). Mit anderen Worten enthalten beide Felder in v Pfeile, von denen jedes ein anderes Paar ablegt.

Hope das macht mehr Sinn. :-)

+0

Können Sie mehr zu V? Warum gilt für Z Rückgabewerte (# 6), aber Nachteile von (cdr x) (cdr y) (# 9) sind immer noch an zwei Referenzen gebunden? – UberJumper

+0

Sowohl z als auch v sind definiert als die Nachteile von zwei bestehenden Werten; aber das Auto von z ist eine Zahl (unveränderlicher Wert), während das Auto von v eine Cons-Zelle ist (veränderbarer Wert, geteilt). Set-Auto! und set-cdr! Modifizieren Sie die Zellen, nicht die Werte, die sie enthalten. – Javier

+2

+1 für das Diagramm. Was hast du benutzt, um das zu machen? –

4

(definieren z (Nachteile (Auto x) (cdr y))) hat eine neue Cons-Zelle für den Kopf von z zugeordnet. Der Kopf von x ist eine andere Cons-Zelle als der Kopf von z, so ändert sich der Inhalt dieser Cons-Zelle für x nicht z.

+0

Wenn Sie davon ausgehen, dass, warum dann nicht Set-cdr! manipulieren v? – UberJumper

+0

überjumper: Es tut es nicht. Es manipuliert die Cons-Zelle, auf die der cdr von x zeigt, die dieselbe ist wie die, auf die das Auto von v zeigt. v selbst bleibt unverändert, ebenso wie x (beim set-cdr! call, meine ich). –

2

Denken Sie daran, dass jede Variable nicht eine Liste enthält, sie halten nur eine cons Zelle. Eine Liste (oder ein Baum) ist eine zusammengesetzte Struktur mehrerer Zellen, von denen einige von mehreren Strukturen gemeinsam genutzt werden können.

Sie denken auch über Zeiger und Referenzen nach, anstatt über Werte nachzudenken, die entweder unveränderliche Werte (wie Zahlen oder Symbole) oder veränderbare Werte (wie Conse oder Strings) sein können und durch Verweis weitergegeben.

ein letzter Punkt zu erinnern: die cons Prozedur immer reserviert eine neue Cons-Zelle.

nach (define z (cons (car x) (cdr y))), hält z eine brandneue Nachteile mit dem gleichen wie x und das gleiche Auto wie x, und die gleiche cdr wie y. wenn du (set-car! x) hast, änderst du einfach die x cons zelle, nicht z.

nach (define v (cons (cdr x) (cdr y))), v hält eine brandneue Nachteile, deren Auto ist der gleiche Wert wie die CDR von x; das ist eine Cons-Zelle. die gleiche cons Zelle als (cdr x). Es wird von den beiden Listen geteilt. Wenn diese gemeinsam genutzte Zelle durch (set-cdr! (cdr x) ...) geändert wird, sind beide Listen betroffen.

0

Die Box und Zeigerdiagramme illustrieren das Beispiel @ Chris gemacht:

(define x (list 1 2 3))    ; => #1 = (1 . #2), #2 = (2 . #3), #3 = (3 .()) 
(define y (list 4 5))    ; => #4 = (4 . #5), #5 = (5 .()) 
(define z (cons (car x) (cdr y))) ; => #6 = (1 . #5) 
(define w (append y z))    ; => #7 = (4 . #8), #8 = (5 . #6) 
(define v (cons (cdr x) (cdr y))) ; => #9 = (#2 . #5) 
(set-car! x 6)      ; => #1 = (6 . #2) 
(set-car! y 7)      ; => #4 = (7 . #5) 
(set-cdr! (cdr x) (list 8))   ; => #2 = (2 . #10), #10 = (8 .()) 

enter image description here