Pregunta

Estoy tratando de comprender la semántica de call/cc en Scheme, y la página de Wikipedia sobre continuaciones muestra el rompecabezas yin-yang como ejemplo:

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

Debería generar @*@**@***@****@..., pero no entiendo por qué;Esperaría que saliera @*@*********...

¿Alguien puede explicar en detalle por qué el rompecabezas yin-yang funciona de la manera en que funciona?

¿Fue útil?

Solución

No creo que entiendo éste totalmente, pero sólo puedo pensar en uno ( muy ondulada a mano) explicación para esto:

  • El primer @ y * se imprimen al yin y yang están obligados por primera vez en la let*. Se aplica (yin yang), y se remonta a la parte superior, justo después de la primera llamada / cc está terminado.
  • El siguiente @ y * se imprimen, y luego otro * se imprime porque esta vez a través, yin es re-unido al valor de la segunda llamada / cc.
  • (yin yang) se aplica de nuevo, pero esta vez está ejecutando en el entorno del yang originales , donde yin se une a la primera llamada / cc, por lo que el control vuelve a imprimir otro @. El argumento yang contiene la continuación que fue re-capturado en la segunda pasada por, que como ya hemos visto, dará lugar a la impresión **. Así que en este tercer paso, @* será impreso, a continuación, esta continuación de doble estrella se invoca la impresión, por lo que termina con 3 estrellas, y luego continuar esta triple estrellas se re-capturado, ...

Otros consejos

Comprensión Esquema

Creo que al menos la mitad del problema con la comprensión de este rompecabezas es la sintaxis de esquema, que la mayoría no están familiarizados.

En primer lugar, yo personalmente encontrar el call/cc x a ser más difícil de comprender que la alternativa equivalente, x get/cc. Todavía llamadas x, pasándole la continuación actual , pero de alguna manera es más susceptible de ser representado en mi circuito cerebral.

Con esto en mente, la construcción se convierte simplemente en (call-with-current-continuation (lambda (c) c)) get-cc. Ahora estamos en esto:

(let* ((yin
         ((lambda (cc) (display #\@) cc) get-cc))
       (yang
         ((lambda (cc) (display #\*) cc) get-cc)) )
    (yin yang))

El siguiente paso es el cuerpo de la lambda interior. (display #\@) cc, en la sintaxis más familiar (al menos para mí) significa print @; return cc;. Mientras estamos en ello, también vamos a reescribir lambda (cc) body como function (arg) { body }, remover un montón de paréntesis, y las llamadas a funciones cambio en el C-como sintaxis, para conseguir esto:

(let*  yin =
         (function(arg) { print @; return arg; })(get-cc)
       yang =
         (function(arg) { print *; return arg; })(get-cc)
    yin(yang))

Está empezando a tener más sentido ahora. Ahora es un pequeño paso para volver a escribir esta completamente en C-como sintaxis (o JavaScript similar, si se prefiere), para conseguir esto:

var yin, yang;
yin = (function(arg) { print @; return arg; })(get-cc);
yang = (function(arg) { print *; return arg; })(get-cc);
yin(yang);

La parte más difícil es ahora más, hemos decodificado esto desde Esquema! Es una broma; solo era difícil porque no tenía ninguna experiencia previa con el Esquema. Por lo tanto, vamos a llegar a averiguar cómo funciona realmente.

Una introducción a continuaciones

Observe el núcleo extrañamente formulado de Yin y Yang: se define una función e inmediatamente llama . Se ve como (function(a,b) { return a+b; })(2, 3), que se puede simplificar a 5. Sin embargo, la simplificación de las llamadas dentro del yin / yang, sería un error, porque no estamos pasándole un valor ordinario. Estamos pasando la función de un continuación .

A continuación es una bestia extraña a primera vista. Considere el programa mucho más sencillo:

var x = get-cc;
print x;
x(5);

Inicialmente x se establece en el objeto continuidad actual (oso conmigo), y print x es ejecutado, la impresión de algo así como <ContinuationObject>. Hasta aquí todo bien.

Sin embargo, una continuación es como una función; se le puede llamar con un argumento. Lo que hace es:. Tomar el argumento, y luego salto a donde quiera que se creó la continuación, la restauración de todo el contexto, y haciéndolo de manera que vuelve get-cc este argumento

En nuestro ejemplo, el argumento es 5, así que básicamente saltar de nuevo a la derecha en el medio de esa declaración var x = get-cc, sólo que esta vez vuelve get-cc 5. Así se convierte en x 5, y la siguiente declaración continúa a imprimir 5. Después de que tratamos de 5(5) llamada, que es un tipo de error, y los errores en el programa.

Observe que llamar a la continuación es un salto , no una llamada. Nunca vuelve de nuevo a donde fue llamado la continuación. Eso es importante.

¿Cómo funciona el programa

Si ha seguido eso, entonces no te hagas ilusiones: esta parte es realmente el más difícil. Aquí está nuestro programa de nuevo, dejando caer las declaraciones de variables porque se trata de pseudo-código de todas formas:

yin = (function(arg) { print @; return arg; })(get-cc);
yang = (function(arg) { print *; return arg; })(get-cc);
yin(yang);

La primera línea de tiempo 1 y 2 son golpeados, son simples ahora: conseguir la continuación, llamar a la función (arg), @ de impresión, a cambio, tienda que la continuación en yin. Lo mismo con yang. Nosotros ahora hemos impreso @*.

A continuación, se llama a la continuación en yin, pasándole yang. Esto nos hace saltar a la línea 1, interior derecho que get-cc, y hacerlo volver yang lugar. El valor de yang ahora se pasa a la función, que imprime @, y luego devuelve el valor de yang. Ahora yin está asignado que la continuación que tiene yang. A continuación simplemente procede a la línea 2: Primeros c / c, * imprimir, almacenar ºe c / c en yang. Ahora tenemos @*@*. Y, por último, vamos a la línea 3.

Recuerde que yin tiene ahora la continuación de cuando la línea 2 se ejecuta en primer lugar. Así que saltamos a la línea 2, la impresión de una segunda * y actualizar yang. Ahora tenemos @*@**. Por último, llama la continuación yin nuevo, que saltará a la línea 1, la impresión de un @. Y así. Francamente, en este momento mi cerebro se produce una excepción OutOfMemory y perder la noción de todo. Pero, al menos, llegamos a @*@**!

Esto es difícil de seguir y aún más difícil de explicar, obviamente. La manera perfecta de hacer esto sería dar un paso a través de él en un depurador que puede representar continuaciones, pero por desgracia, no sé de ninguna. Espero que hayan disfrutado de este; Ciertamente tengo.

Reflexiones primero, posible respuesta al final.

Creo que el código puede ser re-escrito así:

; 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 algunas declaraciones de visualización adicionales para ayudar a ver lo que está sucediendo:

; 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 como esto:

(define (ccc2) (call/cc (lambda (c) c)) )
(define (call-yy2)
    (
        ( (lambda (cc) (display #\@) cc) (ccc2) )
        ( (lambda (cc) (display #\*) cc) (ccc2) )
    )
)

Posible respuesta

Esto puede no ser correcta, pero voy a tener una oportunidad.

Creo que el punto clave es que una 'llamada' continuación devuelve la pila a un estado anterior - como si nada hubiera sucedido. Por supuesto que no sabe que el monitoreo por presentar caracteres @ y *.

En principio definimos yin ser un A continuidad que va a hacer esto:

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)

Pero si llamamos una continuación yang, esto sucede:

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)

Comenzamos aquí.

La primera vez que se obtiene a través yin=A y yang=B como yin y yang están siendo inicializado.

The output is @*

(Ambas continuaciones A y B se calculan.)

Ahora (yin yang) se evalúa como (A B) por primera vez.

sabe lo que hace A. Hace esto:

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')

Ahora (yin yang) se evalúa como (B B').

sabe lo que hace B. Hace esto:

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')

Dado que la pila se restauró hasta el punto en yin=A, (yin yang) se evalúa como (A B').

sabe lo que hace A. Hace esto:

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")

sabe lo que hace B'. Hace esto:

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")

Ahora (yin yang) se evalúa como (B B").

sabe lo que hace B. Hace esto:

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'")

Dado que la pila se restauró hasta el punto en yin=A, (yin yang) se evalúa como (A B'").

.......

Creo que tenemos un patrón ahora.

Cada vez que llamamos (yin yang) nos bucle a través de una pila de continuaciones B hasta que volvamos a cuando yin=A y presentamos @. El ciclo que recorre la pila de continuaciones B escribir un * cada vez.

(I sería muy feliz si esto es más o menos bien!)

Gracias por la pregunta.

El rompecabezas de YinYang está escrito en Scheme.Supongo que conoces la sintaxis básica de Scheme.

Pero supongo que no lo sabes let* o call-with-current-continuation, Explicaré estas dos palabras clave.

Explicar let*

Si ya lo sabe, puede pasar a Explain call-with-current-continuation

let*, que parece let, actúa como let, pero evaluará sus variables definidas (el (yin ...) y (yang ...)) uno por uno y con entusiasmo.Eso significa que primero evaluará yin, y que yang.

Puedes leer más aquí:Usando el esquema Let in

Explicar call-with-current-continuation

Si ya lo sabe, puede pasar a Yin-Yang puzzle.

Es un poco difícil de explicar call-with-current-continuation.Así que usaré una metáfora para explicarlo.

Imagen un mago que conocía un hechizo, que era call-with-current-continuation.Una vez que lanzaba el hechizo, creaba un nuevo universo y se enviaba a sí mismo a él.Pero él podía no hacer nada en el nuevo universo, pero esperando a que alguien lo llame por su nombre.Una vez sido llamado, el mago regresaría al universo original, teniendo al pobre hombre, 'alguien', en la mano, y continuaría con su vida de mago.Si no se le llamaba, cuando terminaba el nuevo universo, el mago también regresaba al universo original.

Ok, seamos más técnicos.

call-with-current-continuation es una función que acepta una función como parámetro.Una vez que llame call-with-current-continuation con una función F, empaquetará el entorno de ejecución actual, que se llama current-continuation, como parámetro C, y envíelo a la función F, y ejecutar F.Entonces todo el programa se convierte en (F C).O ser más JavaScript: F(C);. C actúa como una función.Si C no es llamado F, entonces es un programa ordinario, cuando F devoluciones, call-with-current-continuation tiene un valor como Fel valor de retorno.Pero si C se llama con un parámetro V, cambiará todo el programa nuevamente.El programa vuelve a cambiar a estado cuando call-with-current-continuation sido llamado.Pero ahora call-with-current-continuation produce un valor, que es V.Y el programa continúa.

Tomemos un ejemplo.

(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

El primero display salida 3, de causa.

Pero el segundo display salida 2.¿Por qué?

Vamos a sumergirnos en ello.

Al evaluar (display (call-with-current-continuation f)), primero evaluará (call-with-current-continuation f).Sabemos que cambiará todo el programa para

(f C)

Considerando la definición de f, tiene un (return 2).Debemos evaluar (C 2).Fue entonces cuando el continuation ser llamado.Entonces cambia todo el programa de nuevo a

(display (call-with-current-continuation f))

Pero ahora, call-with-current-continuation tiene valor 2.Entonces el programa se convierte en:

(display 2)

Rompecabezas del Yin-Yang

Veamos el rompecabezas.

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

Hagámoslo más legible.

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

Ejecutemos el programa en nuestro cerebro.

Ronda 0

let* haznos evaluar yin primero. yin es

(f (call-with-current-continuation id))

Entonces evaluamos (call-with-current-continuation id) primero.Empaqueta el entorno actual, al que llamamos C_0 para distinguirlo con otra continuación en la línea de tiempo, y entra en un universo completamente nuevo: id.Pero id solo devoluciones C_0.

Deberíamos Recordar qué C_0 lo es. C_0 es un programa como este:

(let* ((yin
         (f ###))
       (yang
         (g (call-with-current-continuation id))))
      (yin yang))

### es un marcador de posición, que en el futuro se llenará con el valor que C_0 retoma.

Pero id solo devoluciones C_0.No llama C_0.Si llama, entraremos C_0el universo .Pero no fue así, así que continuamos evaluando yin.

(f C_0) ;; yields C_0

f es una función como id, pero tiene un efecto secundario outp @.

Entonces, la salida del programa @ y dejar yin ser C_0.Ahora el programa se convierte en

(let* ((yin C_0)
       (yang
         (g (call-with-current-continuation id))))
      (yin yang))

Después yin evaluado, comenzamos a evaluar yang. yang es

(g (call-with-current-continuation id))

call-with-current-continuation aquí crea otra continuación, llamada C_1. C_1 es:

(let* ((yin C_0)
       (yang
         (g ###)))
      (yin yang))

### es marcador de posición.Tenga en cuenta que en esta continuación, yinse determina el valor (eso es lo que let* hacer).Estamos seguros de que yinel valor es C_0 aquí.

Desde (id C_1) es C_1, entonces yangel valor es

(g C_1)

g tiene un efecto secundario outp *.Así lo hace el programa.

yangel valor es ahora C_1.

Por ahora, hemos mostrado @*

Así que ahora se convierte en:

(let* ((yin C_0)
       (yang C_1))
      (yin yang))

Como ambos yin y yang resueltos, debemos evaluar (yin yang).Es

(C_0 C_1)

¡Santa MIERDA!

Pero finalmente, C_0 se llama.Así que volamos a la C_0 universo y olvídate de estas mierdas.Nunca volveremos a este universo de nuevo.

Ronda 1

C_0 tomar con C_1 atrás.El programa ahora se convierte en (Si olvidas qué C_0 significa, vuelve a verlo):

(let* ((yin
         (f C_1))
       (yang
         (g (call-with-current-continuation id))))
      (yin yang))

Ah, encontramos que yinel valor aún no está determinado.Entonces lo evaluamos.En proceso de evaluación yin, emitimos un @ como fel efecto secundario .Y sabemos yin es C_1 ahora.

Comenzamos a evaluar yang, nos encontramos con call-with-current-continuation otra vez.Somos practicantes.Creamos una continuación C_2 que significa:

(let* ((yin C_1)
       (yang
         (g ###)))
      (yin yang))

Y mostramos un * como g ejecutando.Y venimos aquí

(let* ((yin C_1)
       (yang C_2))
      (yin yang))

Así que tenemos:

(C_1 C_2)

Sabes a dónde vamos.Vamos a C_1el universo .Lo recordamos de memoria(o copiamos y pegamos desde la página web).Es ahora:

(let* ((yin C_0)
       (yang
         (g C_2)))
      (yin yang))

Sabemos en C_1el universo de, yinel valor ha sido determinado.Entonces comenzamos a evaluar yang.A medida que nos practiquen, te diré directamente que se muestra * y se convierte en:

(C_0 C_2)

Ahora hemos impreso @*@**, y vamos a C_0's universo tomando con C_2.

Ronda 2

A medida que nos practiquen, te diré que mostramos'@', yin es C_2, y creamos una nueva continuación C_3, que significa:

(let* ((yin C_2)
       (yang
         (g ###)))
      (yin yang))

Y mostramos *, yang es C_3, y se convierte en

(C_2 C_3)

Y podemos continuar.Pero me detendré aquí, te he mostrado cuáles son las primeras salidas de Yin-Yang puzzle.

Por qué el número de * ¿aumentos?

Ahora tu cabeza está llena de detalles.Te haré un resumen.

Usaré una sintaxis similar a Haskell para simplificar.Y cc es la abreviatura de call-with-current-continuation.

Cuando #C_i# está entre corchetes por #, significa que la continuación se crea aquí. ; salida de medios


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

Si observa cuidadosamente, será obvio para usted que

  1. Hay muchos universos (de hecho infinitos), pero C_0 es el único universo que comenzó por f.Otros es iniciado por g.
  2. C_0 C_n siempre hace una nueva continuación C_max.Es porque C_0 es el primer universo que g cc id tiene no sido ejecutado.
  3. C_0 C_n también mostrar @. C_n C_m que n no es 0 se mostrará *.
  4. De vez en cuando se deduce el programa a C_0 C_n, y demostraré que C_0 C_n está separado por más y más otras expresiones, lo que conduce a @*@**@***...

Un poco de matemáticas

Asumir C_n (¡n != 0) es el número más grande en todas las continuaciones, y luego C_0 C_n se llama.

Suposición:Cuando C_0 C_n se llama, C_n es la continuación numerada máxima actual.

Ahora C_{n+1} es creado por C_0 C_n como esto:

yin = f C_n ; @
yang = g #C_{n+1}#
yin yang

Entonces concluimos que:

Teorema I.Si C_0 C_n se llama, Producirá una continuación C_{n+1}, en el cual yin es C_n.

Entonces el siguiente paso es C_n C_{n+1}.

yin = C_{n-1}
yang = g C_{n+1} ; *
yin yang

La razón por la que yin es C_{n-1} ¿es Eso Cuando C_n siendo creado obedeció Teorema I.

Y entonces C_{n-1} C_{n+1} se llama, y sabemos cuándo C_{n-1} es creado, también obedeció Teorema I.Entonces tenemos C_{n-2} C_{n+1}.

C_{n+1} es la variación interna.Entonces tenemos el segundo teorema:

Teorema II.Si C_n C_m cual n < m y n > 0 se llama, Se convertirá en C_{n-1} C_m.

Y hemos comprobado manualmente C_0 C_1 C_2 C_3.Obedecen a la Suposición y a todos los Teoremas.Y sabemos cómo primero @ y * se crea.

Entonces podemos escribir patrones a continuación.

C_0 C_1 ; @ *
C_[1-0] C_2 ; @ * *
C_[2-0] C_3 ; @ * * *
...

No es tan estricto, pero me gustaría escribir:

Q. E. D.

Como dijo otra respuesta, que (call-with-current-continuation (lambda (c) c)) primera simplificar con get-cc.

(let* ((yin
         ((lambda (cc) (display #\@) cc) get-cc))
       (yang
         ((lambda (cc) (display #\*) cc) get-cc)) )
    (yin yang))

Ahora, los dos lambda son sólo una función idéntica asociado con efectos secundarios. Vamos a llamar a las funciones f (por display #\@) y g (por display #\*).

(let* ((yin (f get-cc))
       (yang (g get-cc)))
    (yin yang))

A continuación necesidad de elaborar el orden de evaluación. Para que quede claro, voy a presentar una "expresión de paso" que hace que cada etapa de evaluación explícita. En primer lugar vamos a preguntar: ¿cuál es la función anterior requiere

Requiere definiciones de f y g. En la expresión de paso, se escribe

s0 f g =>

El primer paso es calcular yin, pero que requieren de evaluar (f get-cc), y la posterior requiere get-cc.

En términos generales, get-cc le da un valor que representa la "continuación actual". Digamos que este es s1 ya que este es el siguiente paso. Así que escribimos

s0 f g => s1 f g ?
s1 f g cc =>

Tenga en cuenta que los parámetros son Scopeless, lo que significa la f y g en s0 y s1 no son necesarias la misma y que son sólo para ser utilizado dentro del paso actual. Esto hace que la información de contexto explícito. Ahora, ¿cuál es el valor de cc? Ya que es "la continuación actual", que es una especie de la misma s1 con f y g unido al mismo valor.

s0 f g => s1 f g (s1 f g)
s1 f g cc =>

Una vez que tenemos cc, podemos evaluar f get-cc. Además, dado que f no se utiliza en el código siguiente, que no tenemos que pasar en este valor.

s0 f g => s1 f g (s1 f g)
s1 f g cc => s2 g (f cc)
s2 g yin =>

El siguiente es el similar para yang. Pero ahora tenemos un valor de más de transmitir:. 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 => 

Finalmente, el último paso es aplicar 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

Esto terminó el constructo de expresión paso. Traducir de nuevo al esquema es simple:

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

El orden de evaluación detallada (aquí el lambda dentro del cuerpo de s2 se expresó simplemente como s3 yin evaluación parcial en lugar de (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)))
=> ...

(Recuerde que, al evaluar s2 o s4, el parámetro se va a evaluar primero

Esta un viejo rompecabezas desde el maestro de la ofuscación David Madore, quien creado unlambda. El rompecabezas se ha discutido varias comp.lang.scheme veces.

Una buena solución de Taylor Campbell: https://groups.google.com/d/msg/comp .lang.scheme / pUedvrKYY5w / uIjTc_T1LOEJ

El post original de David Madore (1999): https://groups.google.com/d/msg/comp .lang.scheme / Fysq_Wplxsw / awxEZ_uxW20J

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top