La Compréhension De Régime
Je pense qu'au moins la moitié du problème avec la compréhension de ce casse-tête est le Régime de la syntaxe, dont la plupart ne sont pas familiers avec.
Tout d'abord, personnellement, je trouve l' call/cc x
d'être plus difficile à comprendre que l'alternative équivalente, x get/cc
. - Il encore des appels de x, en lui transmettant le courant de la poursuite, mais d'une certaine manière est plus susceptible d'être représentée dans mes circuits du cerveau.
Avec cela à l'esprit, la construction (call-with-current-continuation (lambda (c) c))
devient simplement get-cc
. Nous sommes maintenant à ceci:
(let* ((yin
((lambda (cc) (display #\@) cc) get-cc))
(yang
((lambda (cc) (display #\*) cc) get-cc)) )
(yin yang))
La prochaine étape est le corps de l'intérieur de la lambda. (display #\@) cc
, dans le plus familier de la syntaxe (pour moi, de toute façon) désigne print @; return cc;
. Pendant que nous y sommes, nous allons également réécrire lambda (cc) body
comme function (arg) { body }
, enlever un tas de parenthèses, et le changement des appels de fonction pour le C-comme la syntaxe, pour obtenir ceci:
(let* yin =
(function(arg) { print @; return arg; })(get-cc)
yang =
(function(arg) { print *; return arg; })(get-cc)
yin(yang))
Il commence à faire plus de sens maintenant. C'est maintenant une petite étape à réécrire entièrement en C-syntaxe (ou JavaScript-comme, si vous préférez), pour obtenir ceci:
var yin, yang;
yin = (function(arg) { print @; return arg; })(get-cc);
yang = (function(arg) { print *; return arg; })(get-cc);
yin(yang);
La partie la plus difficile est maintenant terminée, nous avons décodé ce de Régime! C'est juste une blague; c'était dur parce que je n'avais aucune expérience précédente avec le Régime. Donc, nous allons apprendre à essayer de comprendre comment cela fonctionne réellement.
Une couche d'apprêt sur les continuations
Observer l'étrangement formulée de base du yin et du yang: il définit une fonction , puis immédiatement à l'appelle. Ça ressemble à de la (function(a,b) { return a+b; })(2, 3)
, ce qui peut être simplifié à l' 5
. Mais de simplifier les appels à l'intérieur de yin/yang serait une erreur, car nous ne sommes pas en passant une valeur ordinaire. Nous passons à la fonction d'une poursuite.
Une suite est une drôle de bête à première vue. Envisager le plus simple programme:
var x = get-cc;
print x;
x(5);
Initialement, x
est défini à la poursuite de l'objet (l'ours avec moi), et print x
est exécuté, l'impression de quelque chose comme <ContinuationObject>
. So far So good.
Mais une continuation, c'est comme une fonction; elle peut être appelée avec un argument. Ce qu'il fait est: prendre l'argument, puis sauter à l'endroit où la poursuite a été créé, la restauration de tout contexte, et de faire en sorte qu' get-cc
retourne cet argument.
Dans notre exemple, l'argument est - 5
, nous avons donc essentiellement de sauter de nouveau dans le milieu de l' var x = get-cc
déclaration, seulement cette fois - get-cc
retours 5
. Donc, x
devient 5
, et la prochaine déclaration va à l'impression 5. Après que nous essayons de faire appel 5(5)
, ce qui est une erreur de type, et le programme se bloque.
Observer que l'appel de la poursuite est un saut, pas un appel. Il ne revient jamais en arrière à l'endroit où la poursuite a été appelé. Ce qui est important.
Comment fonctionne le programme
Si vous avez suivi, puis ne pas obtenir vos espoirs vers le haut: cette partie est vraiment le plus dur. Voici notre programme de nouveau, la suppression des déclarations de variables parce que c'est de la pseudo-code de toute façon:
yin = (function(arg) { print @; return arg; })(get-cc);
yang = (function(arg) { print *; return arg; })(get-cc);
yin(yang);
La première ligne du temps 1 et 2 sont touchés, ils sont simple maintenant: obtenir la poursuite, l'appel de la fonction(arg), imprimer @
, le rendement, le magasin de poursuite en yin
. Même avec yang
. Nous avons maintenant imprimé @*
.
Ensuite, nous appelons à la poursuite en yin
, passant yang
. Cela nous fait sauter à la ligne 1, à droite à l'intérieur que les cc, et le faire revenir yang
à la place. La valeur de yang
est maintenant passé dans la fonction, ce qui imprime @
, puis renvoie la valeur de yang
. Maintenant, yin
est affectée à la poursuite qu' yang
a. Ensuite, nous avons juste passer à la ligne 2: c/c, imprimer *
, stocker le c/c en yang
. Nous avons maintenant @*@*
. Et enfin, nous allons à la ligne 3.
Rappelez-vous que yin
maintenant a la poursuite de lorsque la ligne 2 a été exécuté. Afin de nous sauter à la ligne 2, l'impression d'un second *
et la mise à jour de yang
. Nous avons maintenant @*@**
. Enfin, l'appel de l' yin
continuation de nouveau, qui va sauter à la ligne 1, l'impression d'un @
. Et ainsi de suite. Franchement, à ce stade, mon cerveau déclenche une exception OutOfMemory et j'en perds la trace de tout. Mais nous avons au moins à l' @*@**
!
C'est dur à suivre, et encore plus difficile à expliquer, évidemment. La meilleure façon de le faire serait d'étape à travers elle dans un débogueur qui peut représenter des suites, mais hélas, je ne sais pas du tout. J'espère que vous avez apprécié ce; je n'ai certainement.