Come funziona il puzzle di yin-yang?
Domanda
Sto cercando di cogliere la semantica di call / cc in Scheme, e la pagina di Wikipedia su continuazioni spettacoli puzzle yin-yang come esempio:
(let* ((yin
((lambda (cc) (display #\@) cc) (call-with-current-continuation (lambda (c) c))))
(yang
((lambda (cc) (display #\*) cc) (call-with-current-continuation (lambda (c) c)))) )
(yin yang))
Si deve @*@**@***@****@...
uscita,
ma io non capisco perché; Mi aspetto che per l'uscita @*@*********
...
Qualcuno può spiegare in dettaglio perché il puzzle yin-yang funziona il modo in cui funziona?
Soluzione
Non credo di comprendere appieno questo, ma posso solo pensare a una ( molto a mano ondulato) spiegazione per questo:
- La prima @ e * vengono stampati quando
yin
eyang
sono prima tenuti inlet*
.(yin yang)
viene applicato, e risale verso l'alto, subito dopo la prima chiamata / cc è terminata. - Il prossimo @ e * sono stampati, poi un altro * è stampato perché questa volta attraverso,
yin
è ri-legato al valore della seconda chiamata / cc. -
(yin yang)
viene applicato di nuovo, ma questa volta è in esecuzione nell'ambiente delyang
originale , doveyin
è legato alla prima chiamata / cc, così il controllo torna a stampare un altro @. L'argomentoyang
contiene la continuazione che è stato nuovamente catturato sul secondo passaggio attraverso, che, come abbiamo già visto, si tradurrà in stampa**
. Così in questo terzo passaggio,@*
verrà stampato, quindi questa continuazione stella doppia-stampa viene invocata, in modo che finisce con 3 stelle, e quindi questa triplice stelle continuazione è ri-catturato, ...
Altri suggerimenti
Understanding Scheme
Credo che almeno la metà del problema con la comprensione di questo puzzle è la sintassi Scheme, che la maggior parte non hanno familiarità con.
Prima di tutto, io personalmente trovare il call/cc x
ad essere più difficile da comprendere che l'alternativa equivalente, x get/cc
. E 'ancora chiamate x, passandogli la continuazione corrente , ma in qualche modo è più suscettibili di essere rappresentati nel mio circuiti cerebrali.
Con questo in mente, il (call-with-current-continuation (lambda (c) c))
costrutto diventa semplicemente get-cc
. Ora siamo a questo:
(let* ((yin
((lambda (cc) (display #\@) cc) get-cc))
(yang
((lambda (cc) (display #\*) cc) get-cc)) )
(yin yang))
Il passo successivo è il corpo del lambda interna. (display #\@) cc
, nella sintassi più familiare (per me, comunque) significa print @; return cc;
. Mentre noi siamo, diamo anche riscrivere lambda (cc) body
come function (arg) { body }
, rimuovere un po 'di parentesi, e chiamate di funzione modifica al C-come la sintassi, per arrivare a questo:
(let* yin =
(function(arg) { print @; return arg; })(get-cc)
yang =
(function(arg) { print *; return arg; })(get-cc)
yin(yang))
Sta cominciando ad avere più senso ora. Ora è un piccolo passo per riscrivere questo completamente in C-sintassi simile (o JavaScript simile, se si preferisce), per arrivare a questo:
var yin, yang;
yin = (function(arg) { print @; return arg; })(get-cc);
yang = (function(arg) { print *; return arg; })(get-cc);
yin(yang);
La parte più difficile è finita, abbiamo decodificato questo da Scheme! Stavo solo scherzando; era difficile solo perché non avevo alcuna esperienza precedente con Scheme. Quindi, andiamo a capire come funziona in realtà.
Un primer su continuazioni
Osservare il nucleo stranamente formulata di yin e yang: definisce una funzione e poi immediatamente chiama . Sembra proprio come (function(a,b) { return a+b; })(2, 3)
, che può essere semplificata in 5
. Ma semplificare le chiamate all'interno di yin / yang sarebbe un errore, perché non stiamo passando un valore normale. Stiamo passando la funzione di un continuazione .
Una continuazione è una bestia strana a prima vista. Prendere in considerazione il programma molto più semplice:
var x = get-cc;
print x;
x(5);
Inizialmente x
è impostato l'oggetto prosecuzione corrente (orso con me), e print x
viene eseguito, la stampa qualcosa come <ContinuationObject>
. Fin qui tutto bene.
Ma una continuazione è come una funzione; può essere chiamato con un argomento. Ciò che fa è:. Prendere l'argomento, e quindi salto per ovunque che la continuazione è stato creato, il ripristino di tutto contesto, e facendo in modo che i rendimenti get-cc
questo argomento
Nel nostro esempio, l'argomento è 5
, così abbiamo essenzialmente salto indietro proprio nel bel mezzo di quella dichiarazione var x = get-cc
, solo che questa volta torna get-cc
5
. Così x
diventa 5
, e l'istruzione successiva continua a stampare 5. Dopo che cerchiamo di chiamata 5(5)
, che è un errore di tipo, e il programma si blocca.
Si noti che chiamare la continuazione è un salto , non è una chiamata. Non è mai ritorna al punto in cui è stato chiamato il proseguimento. Questo è importante.
Come funziona il programma
Se avete seguito che, allora non ci si illuda: questa parte è davvero la più difficile. Ecco il nostro programma di nuovo, lasciando cadere le dichiarazioni di variabili, perché questo è pseudo-codice in ogni caso:
yin = (function(arg) { print @; return arg; })(get-cc);
yang = (function(arg) { print *; return arg; })(get-cc);
yin(yang);
La prima linea del tempo 1 e 2 sono colpiti, sono semplici ora: ottenere la continuazione, chiamare la funzione (arg), @
stampa, cambio, negozio che la continuazione in yin
. Stessa cosa con yang
. Ora abbiamo stampato @*
.
Avanti, chiamiamo alla prosecuzione nel yin
, passando yang
. Questo ci fa saltare per la linea 1, proprio all'interno che get-cc, e farla tornare yang
invece. Il valore di yang
è ora passato alla funzione, che stampa @
, e poi restituisce il valore di yang
. Ora yin
è assegnato che la continuazione che yang
ha. Poi abbiamo appena procede alla linea 2: get c / c, *
stampare, archiviare °e c / c in yang
. Ora abbiamo @*@*
. E, infine, andiamo a linea 3.
Ricordate che yin
ora ha la continuazione dal momento in cui è stato eseguito prima linea 2. Così saltiamo alla linea 2, la stampa di un secondo *
e l'aggiornamento yang
. Ora abbiamo @*@**
. Infine, chiamare di nuovo la continuazione yin
, che salterà per la linea 1, la stampa di un @
. E così via. Francamente, a questo punto il mio cervello genera un'eccezione OutOfMemory e perdo traccia di tutto ciò. Ma almeno abbiamo avuto modo di @*@**
!
Questo è difficile da seguire e ancora più difficile da spiegare, ovviamente. Il modo perfetto per fare questo sarebbe fare un passo attraverso di essa in un debugger che può rappresentare continuazioni, ma ahimè, non so di qualsiasi. Spero che abbiate goduto di questo; Certamente ho.
Musings prima, possibile risposta alla fine.
Credo che il codice può essere riscritto in questo modo:
; call (yin yang)
(define (yy yin yang) (yin yang))
; run (call-yy) to set it off
(define (call-yy)
(yy
( (lambda (cc) (display #\@) cc) (call/cc (lambda (c) c)) )
( (lambda (cc) (display #\*) cc) (call/cc (lambda (c) c)) )
)
)
O con alcune dichiarazioni di visualizzazione in più per aiutare a vedere che cosa sta accadendo:
; create current continuation and tell us when you do
(define (ccc)
(display "call/cc=")
(call-with-current-continuation (lambda (c) (display c) (newline) c))
)
; call (yin yang)
(define (yy yin yang) (yin yang))
; run (call-yy) to set it off
(define (call-yy)
(yy
( (lambda (cc) (display "yin : ") (display #\@) (display cc) (newline) cc)
(ccc) )
( (lambda (cc) (display "yang : ") (display #\*) (display cc) (newline) cc)
(ccc) )
)
)
O come questa:
(define (ccc2) (call/cc (lambda (c) c)) )
(define (call-yy2)
(
( (lambda (cc) (display #\@) cc) (ccc2) )
( (lambda (cc) (display #\*) cc) (ccc2) )
)
)
possibile risposta ??b>
Questo non può essere giusto, ma io avere un andare.
Credo che il punto chiave è che un 'chiamato' la continuazione restituisce la pila a uno stato precedente - come se non altro fosse accaduto. Naturalmente non sa che abbiamo il monitoraggio è visualizzando personaggi @
e *
.
Inizialmente definiamo yin
essere un A
continuazione che farà questo:
1. restore the stack to some previous point
2. display @
3. assign a continuation to yin
4. compute a continuation X, display * and assign X to yang
5. evaluate yin with the continuation value of yang - (yin yang)
Ma se chiamiamo una continuazione yang
, questo accade:
1. restore the stack to some point where yin was defined
2. display *
3. assign a continuation to yang
4. evaluate yin with the continuation value of yang - (yin yang)
Si comincia qui.
La prima volta che si ottiene attraverso yin=A
e yang=B
come yin
e yang
vengono inizializzati.
The output is @*
(Sia continuazioni A
e B
sono calcolati.)
Ora (yin yang)
viene valutato come (A B)
per la prima volta.
Sappiamo cosa A
fa. Lo fa:
1. restores the stack - back to the point where yin and yang were being initialised.
2. display @
3. assign a continuation to yin - this time, it is B, we don't compute it.
4. compute another continuation B', display * and assign B' to yang
The output is now @*@*
5. evaluate yin (B) with the continuation value of yang (B')
Ora (yin yang)
viene valutato come (B B')
.
Sappiamo cosa B
fa. Lo fa:
1. restore the stack - back to the point where yin was already initialised.
2. display *
3. assign a continuation to yang - this time, it is B'
The output is now @*@**
4. evaluate yin with the continuation value of yang (B')
Dal momento che lo stack è stato riportato al punto in cui yin=A
, (yin yang)
viene valutato come (A B')
.
Sappiamo cosa A
fa. Lo fa:
1. restores the stack - back to the point where yin and yang were being initialised.
2. display @
3. assign a continuation to yin - this time, it is B', we don't compute it.
4. compute another continuation B", display * and assign B" to yang
The output is now @*@**@*
5. evaluate yin (B') with the continuation value of yang (B")
Sappiamo cosa B'
fa. Lo fa:
1. restore the stack - back to the point where yin=B.
2. display *
3. assign a continuation to yang - this time, it is B"
The output is now @*@**@**
4. evaluate yin (B) with the continuation value of yang (B")
Ora (yin yang)
viene valutato come (B B")
.
Sappiamo cosa B
fa. Lo fa:
1. restore the stack - back to the point where yin=A and yang were being initialised.
2. display *
3. assign a continuation to yang - this time, it is B'"
The output is now @*@**@***
4. evaluate yin with the continuation value of yang (B'")
Dal momento che lo stack è stato riportato al punto in cui yin=A
, (yin yang)
viene valutato come (A B'")
.
.......
Credo che abbiamo un modello ora.
Ogni volta che noi chiamiamo (yin yang)
abbiamo ciclo attraverso una pila di continuazioni B
fino ad arrivare indietro a quando yin=A
e visualizziamo @
. Il ciclo che attraverso la pila di continuazioni B
la scrittura di un *
ogni volta.
(sarei davvero felice se questo è più o meno a destra!)
Grazie per la domanda.
YinYang puzzle è scritto nello Schema. Suppongo che si conosce la sintassi di base dello schema.
Ma suppongo che non sai let*
o call-with-current-continuation
, spiegherò queste due parole chiave.
Spiegare let*
Se sai già che, si può saltare a Explain call-with-current-continuation
let*
, che si presenta come let
, agisce come let
, ma valuterà le sue variabili definite (il (yin ...)
e (yang ...)
) uno per uno e con entusiasmo. Ciò significa, in primo luogo valutare yin
, e di yang
.
Si può leggere di più qui: Utilizzando Let nello Schema
Spiegare call-with-current-continuation
Se sai già che, si può passare alla Yin-Yang puzzle
.
E 'un po' difficile da spiegare call-with-current-continuation
. Quindi userò una metafora per spiegare.
Immagine di un mago che conosceva un incantesimo, che era call-with-current-continuation
. Una volta lanciato l'incantesimo, avrebbe creato un nuovo universo, e lo sé inviare ad esso. Ma poteva fare nulla nel nuovo universo, ma in attesa di qualcuno che chiama il suo nome. Una volta che stato chiamato , la procedura guidata sarebbe tornato per l'universo originale, avendo il povero ragazzo - 'qualcuno' - in mano, e andare sulla sua vita procedura guidata. Se non è stato chiamato, quando il nuovo universo si è conclusa, la procedura guidata anche restituito all'universo originale.
Ok, cerchiamo di essere più tecnico.
call-with-current-continuation
è una funzione che accetta una funzione come parametro. Una volta che si chiama call-with-current-continuation
con una funzione F
, sarà imballare l'ambiente in esecuzione corrente, che si chiama current-continuation
, come parametro C
, ed inviarlo alla funzione F
, ed eseguire F
. Così l'intero programma diventa (F C)
. O essere più JavaScript: F(C);
. C
agisce come una funzione. Se C
non è rimessa in F
, allora è un programma ordinario, al ritorno F
, call-with-current-continuation
ha un valore come valore di ritorno di F
. Ma se C
viene chiamato con un parametro V
, cambierà di nuovo l'intero programma. Il programma cambia di nuovo ad un Stato quando call-with-current-continuation
stato chiamato. Ma ora call-with-current-continuation
produce un valore, che è V
. E il programma continua.
Facciamo un esempio.
(define (f return)
(return 2)
3)
(display (f whatever)) ;; 3
(display (call-with-current-continuation f)) ;; 2
(display (call-with-current-continuation (lambda (x) 4))) ;; 4
La prima uscita display
3
, di causa.
Ma la seconda uscita display
2
. Perché?
tuffo Let in esso.
Nel valutare (display (call-with-current-continuation f))
, in primo luogo valutare (call-with-current-continuation f)
. Sappiamo che cambierà l'intero programma a
(f C)
Considerando la definizione f
, ha un (return 2)
. Dobbiamo valutare (C 2)
. Questo è quando il continuation
chiamato. Quindi modificare il programma intero torna a
(display (call-with-current-continuation f))
Ma ora, call-with-current-continuation
ha valore 2
. Così il programma diventa:
(display 2)
Yin-Yang di puzzle
Diamo un'occhiata al puzzle.
(let* ((yin
((lambda (cc) (display #\@) cc) (call-with-current-continuation (lambda (c) c))))
(yang
((lambda (cc) (display #\*) cc) (call-with-current-continuation (lambda (c) c)))))
(yin yang))
make Ammettiamolo più leggibile.
(define (id c) c)
(define (f cc) (display #\@) cc)
(define (g cc) (display #\*) cc)
(let* ((yin
(f (call-with-current-continuation id)))
(yang
(g (call-with-current-continuation id))))
(yin yang))
corsa di lasciare che il programma nel nostro cervello.
rotonda 0
let*
fanno di valutare yin
prima. yin
è
(f (call-with-current-continuation id))
Quindi valutiamo prima (call-with-current-continuation id)
. Imballa contesto attuale, che noi chiamiamo C_0
distinguere con altri prosecuzione nel time-line, ed entra in un nuovo universo: id
. Ma id
restituisce solo C_0
.
Dobbiamo ricordare ciò che è C_0
. C_0
è un programma come questo:
(let* ((yin
(f ###))
(yang
(g (call-with-current-continuation id))))
(yin yang))
###
è un segnaposto, che in futuro sarà riempito dal valore che C_0
riprende.
Ma id
restituisce solo C_0
. Essa non chiama C_0
. Se si chiama, entreremo l'universo di C_0
. Ma non lo fece, così continuiamo a valutare yin
.
(f C_0) ;; yields C_0
f
è una funzione come id
, ma ha un effetto collaterale -. Emettere @
Così il @
output del programma e lasciare yin
di essere C_0
. Ora il programma diventa ??p>
(let* ((yin C_0)
(yang
(g (call-with-current-continuation id))))
(yin yang))
Dopo yin
valutato, iniziamo a valutare yang
. yang
è
(g (call-with-current-continuation id))
call-with-current-continuation
qui creare un altro seguito, di nome C_1
. C_1
è:
(let* ((yin C_0)
(yang
(g ###)))
(yin yang))
###
è segnaposto. Si noti che in questo seguito, il valore di yin
è determinato (che è quello che let*
fare). Siamo sicuri che il valore di yin
è C_0
qui.
Dal (id C_1)
è C_1
, quindi il valore di yang
è
(g C_1)
g
ha un effetto collaterale - output *
. Così fa il programma.
Il valore di yang
è ora C_1
.
@*
A questo punto, abbiamo visualizzato
Così ora diventa:
(let* ((yin C_0)
(yang C_1))
(yin yang))
Per quanto sia yin
e yang
sono risolti, dovremmo valutare (yin yang)
. E '
(C_0 C_1)
Santo sh * t!
Ma alla fine, C_0
si chiama. Così voliamo nell'universo C_0
e dimenticare tutto su questi sh * ts. Non riusciremo mai a tornare a questo universo di nuovo.
Round 1
take C_0
con schienale C_1
. Il programma diventa ora (Se si dimentica quello che sta per C_0
, torna a vedere):
(let* ((yin
(f C_1))
(yang
(g (call-with-current-continuation id))))
(yin yang))
Ah, troviamo il valore di quel yin
non è ancora determinato. Quindi valutiamo esso. Nel processo di valutazione yin
, abbiamo uscita un @
come effetto collaterale di f
. E sappiamo yin
è C_1
ora.
Iniziamo a valutare yang
, ci siamo imbattuti in call-with-current-continuation
di nuovo. Ci sono praticati. Creiamo un C_2
continuazione, che significa:
(let* ((yin C_1)
(yang
(g ###)))
(yin yang))
E abbiamo visualizzare un *
come g
esecuzione. E veniamo qui
(let* ((yin C_1)
(yang C_2))
(yin yang))
Così abbiamo ottenuto:
(C_1 C_2)
Non si sa dove stiamo andando. Stiamo per l'universo di C_1
. Ricordiamo a memoria (o copiare e incollare dalla pagina web). E 'ora:
(let* ((yin C_0)
(yang
(g C_2)))
(yin yang))
Sappiamo nell'universo di C_1
, il valore di yin
è stata determinata. Quindi iniziamo a valutare yang
. Come ci si praticano, io direttamente dirvi che visualizza *
e diventa:
(C_0 C_2)
@*@**
Ora abbiamo stampato, e ci accingiamo a presa nell'universo di C_0
con C_2
.
Round 2
Come ci si praticano, io vi dirà che visualizziamo '@', yin
è C_2
, e creiamo un nuovo C_3
continuazione, che significa:
(let* ((yin C_2)
(yang
(g ###)))
(yin yang))
E visualizziamo *
, yang
è C_3
, e diventa ??p>
(C_2 C_3)
E possiamo continuare. Ma mi fermo qui, vi ho mostrato la prima cosa che più uscite di Yin-Yang di puzzle sono.
Perché il numero di aumenti *
?
Ora la testa è piena di dettagli. Farò una sintesiper voi.
I userà un Haskell come sintassi per semplificare. E cc
è l'abbreviazione di call-with-current-continuation
.
Quando #C_i#
viene variata da #
, significa che la prosecuzione è creare qui. mezzi ;
uscita ??p>
yin = f cc id
yang = g cc id
yin yang
---
yin = f #C_0# ; @
yang = g cc id
yin yang
---
yin = C_0
yang = g #C_1# ; *
yin yang
---
C_0 C_1
---
yin = f C_1 ; @
yang = g #C_2# ; *
yin yang
---
C_1 C_2
---
yin = C_0
yang = g C_2 ; *
yin yang
---
C_0 C_2
---
yin = f C_2 ; @
yang = g #C_3#; *
yin yang
---
C_2 C_3
---
yin = C_1
yang = g C_3 ; *
yin yang
---
C_1 C_3
---
yin = C_0
yang = g C_3 ; *
yin yang
---
C_0 C_3
Se si osserva con attenzione, sarà ovvio che
- Ci sono un sacco di universi (in realtà infinita), ma
C_0
è l'unico universo che ha iniziato daf
. Altri sono iniziate dag
. -
C_0 C_n
fa sempre un nuovoC_max
continuazione. È perchéC_0
è il primo universo cheg cc id
ha non stato eseguito. -
C_0 C_n
visualizzare anche@
.C_n C_m
cui n non è 0 visualizzerà*
. - di volta in volta il programma si deduce a
C_0 C_n
, e io dimostrare cheC_0 C_n
è separato da un'espressione più e più altri, che porta a@*@**@***...
Un po 'di matematica
Si supponga (n! = 0) è il più grande numero in tutte le continuazioni, e quindi C_0 C_n
si chiama .
Assunzione:. Quando C_0 C_n
viene chiamato, C_n
è la corrente massima prosecuzione numerata ??strong>
Ora viene creato da C_0 C_n
in questo modo:
yin = f C_n ; @
yang = g #C_{n+1}#
yin yang
Quindi, possiamo concludere che:
Teorema I. Se C_0 C_n
si chiama, si produrrà una continuazione , nella quale è yin
C_n
.
Poi passo successivo è C_n C_{n+1}
.
yin = C_{n-1}
yang = g C_{n+1} ; *
yin yang
Il motivo per cui è yin
C_{n-1}
è che quando C_n
essere creato obbedì Teorema I .
E poi C_{n-1} C_{n+1}
si chiama, e sappiamo quando si crea C_{n-1}
, ma anche obbedito Teorema I . Così abbiamo C_{n-2} C_{n+1}
.
C_{n+1}
è l'in-variante. Così abbiamo il secondo teorema:
teorema II. Se C_n C_m
che n < m
e n > 0
si chiama, diventerà C_{n-1} C_m
.
E abbiamo manualmente controllati C_0
C_1
C_2
C_3
. Obbediscono dell'Assunta e tutti i teoremi. E sappiamo come primo @
e *
si viene a creare.
Quindi possiamo scrivere i modelli qui di seguito.
C_0 C_1 ; @ *
C_[1-0] C_2 ; @ * *
C_[2-0] C_3 ; @ * * *
...
Non così severo, ma mi piacerebbe scrivere:
Q.E.D.
Come un'altra risposta, ha detto, abbiamo (call-with-current-continuation (lambda (c) c))
primo semplificare con get-cc
.
(let* ((yin
((lambda (cc) (display #\@) cc) get-cc))
(yang
((lambda (cc) (display #\*) cc) get-cc)) )
(yin yang))
Ora, i due lambda sono solo una funzione identica associato con effetti collaterali. Chiamiamo quelle funzioni f
(per display #\@
) e g
(per display #\*
).
(let* ((yin (f get-cc))
(yang (g get-cc)))
(yin yang))
Avanti abbiamo bisogno di lavorare fuori l'ordine di valutazione. Per essere chiari, io introdurrà una "espressione step" che rende ogni fase di valutazione esplicita. Prima di tutto chiedono: qual è la funzione di cui sopra richiede
Si richiede definizioni di f
e g
. In fase di espressione, scriviamo
s0 f g =>
Il primo passo è quello di calcolare yin
, ma che richiedono la valutazione di (f get-cc)
, e la successiva richiede get-cc
.
In parole povere, get-cc
ti dà un valore che rappresenta la "continuazione corrente". Diciamo che questo è s1
poiché questo è il passo successivo. Così scriviamo
s0 f g => s1 f g ?
s1 f g cc =>
Si noti che i parametri sono scopeless, che significa che la f
e g
in s0
e s1
non sono necessari lo stesso e sono da utilizzare solo all'interno del passo corrente. Questo rende le informazioni di contesto esplicito. Ora, qual è il valore per cc
? Poiché è "continuazione di", è sorta la stessa s1
con f
e g
vincolato allo stesso valore.
s0 f g => s1 f g (s1 f g)
s1 f g cc =>
Una volta che abbiamo cc
, possiamo valutare f get-cc
. Inoltre, dal momento che f
non viene utilizzato nel codice seguente, non abbiamo di trasmettere questo valore.
s0 f g => s1 f g (s1 f g)
s1 f g cc => s2 g (f cc)
s2 g yin =>
Il prossimo è il simile per yang
. Ma ora abbiamo un valore di più di trasmettere:. yin
s0 f g => s1 f g (s1 f g)
s1 f g cc => s2 g (f cc)
s2 g yin => s3 g yin (s3 g yin)
s3 g yin cc => s4 yin (g cc)
s4 yin yang =>
Infine, l'ultimo passo è quello di applicare yang
a yin
.
s0 f g => s1 f g (s1 f g)
s1 f g cc => s2 g (f cc)
s2 g yin => s3 g yin (s3 g yin)
s3 g yin cc => s4 yin (g cc)
s4 yin yang => yin yang
Questa finito il costrutto di espressione passo. Tradurre di nuovo a schema è semplice:
(let* ([s4 (lambda (yin yang) (yin yang))]
[s3 (lambda (yin cc) (s4 yin (g cc))]
[s2 (lambda (yin) (s3 yin ((lambda (cc) (s3 yin cc))))]
[s1 (lambda (cc) (s2 (f cc)))])
(s1 s1))
L'ordine valutazione dettagliata (qui lambda all'interno del corpo di s2
stato semplicemente espressa come parziale s3 yin
valutazione anziché (lambda (cc) (s3 yin cc))
):
(s1 s1)
=> (s2 (f s1))
=> @|(s2 s1)
=> @|(s3 s1 (s3 s1))
=> @|(s4 s1 (g (s3 s1)))
=> @*|(s4 s1 (s3 s1))
=> @*|(s1 (s3 s1))
=> @*|(s2 (f (s3 s1)))
=> @*@|(s2 (s3 s1))
=> @*@|(s2 (s3 s1))
=> @*@|(s3 (s3 s1) (s3 (s3 s1)))
=> @*@|(s4 (s3 s1) (g (s3 (s3 s1))))
=> @*@*|(s4 (s3 s1) (s3 (s3 s1)))
=> @*@*|(s3 s1 (s3 (s3 s1)))
=> @*@*|(s4 s1 (g (s3 (s3 s1))))
=> @*@**|(s4 s1 (s3 (s3 s1)))
=> @*@**|(s1 (s3 (s3 s1)))
=> ...
(Ricordate, quando si valutano s2
o s4
, il parametro sta per essere valutati prima
Questo un vecchio puzzle Da maestro di offuscamento David Madore, che creato Unlambda. Il puzzle è stato discusso comp.lang.scheme diversi volte.
Una soluzione piacevole da Taylor Campbell: https://groups.google.com/d/msg/comp .lang.scheme / pUedvrKYY5w / uIjTc_T1LOEJ
Il post originale da David Madore (1999): https://groups.google.com/d/msg/comp .lang.scheme / Fysq_Wplxsw / awxEZ_uxW20J