Réponse courte
Contournez les règles d'évaluation par défaut et n'évaluez pas l'expression (symbole ou s-exp), en la transmettant à la fonction exactement telle qu'elle a été saisie.
Réponse longue: la règle d'évaluation par défaut
Lorsqu'une fonction régulière (j'y reviendrai plus tard) est appelée, tous les arguments qui lui sont passés sont évalués. Cela signifie que vous pouvez écrire ceci:
(* (+ a 2)
3)
Qui à son tour évalue (+ a 2)
, en évaluant a
et 2. La valeur du symbole a
est recherchée dans le jeu de liaisons de variable courant, puis remplacée. Say a
est actuellement lié à la valeur 3:
(let ((a 3))
(* (+ a 2)
3))
Nous obtiendrions (+ 3 2)
, + est alors invoqué sur 3 et 2 donnant 5. Notre forme originale (* 5 3)
donne maintenant 15.
Expliquez quote
déjà!
Bien. Comme vu ci-dessus, tous les arguments d'une fonction sont évalués, donc si vous souhaitez passer le symbole a
et non sa valeur, vous ne voulez pas l'évaluer. Les symboles Lisp peuvent doubler leurs valeurs et les marqueurs là où vous dans d'autres langues auriez utilisé des chaînes, telles que des clés de tables de hachage.
C'est là quote
qu'intervient. Supposons que vous souhaitiez tracer les allocations de ressources à partir d'une application Python, mais que vous fassiez plutôt le tracé en Lisp. Demandez à votre application Python de faire quelque chose comme ceci:
print("'(")
while allocating:
if random.random() > 0.5:
print(f"(allocate {random.randint(0, 20)})")
else:
print(f"(free {random.randint(0, 20)})")
...
print(")")
Vous donnant une sortie ressemblant à ceci (légèrement jolie):
'((allocate 3)
(allocate 7)
(free 14)
(allocate 19)
...)
Vous vous souvenez de ce que j'ai dit quote
("cocher") pour que la règle par défaut ne s'applique pas? Bien. Ce qui se passerait autrement, c'est que les valeurs de allocate
et free
sont recherchées, et nous ne voulons pas de cela. Dans notre Lisp, nous souhaitons faire:
(dolist (entry allocation-log)
(case (first entry)
(allocate (plot-allocation (second entry)))
(free (plot-free (second entry)))))
Pour les données données ci-dessus, la séquence suivante d'appels de fonction aurait été effectuée:
(plot-allocation 3)
(plot-allocation 7)
(plot-free 14)
(plot-allocation 19)
Mais qu'en est-il list
?
Eh bien, parfois vous ne voulez évaluer les arguments. Disons que vous avez une fonction astucieuse manipulant un nombre et une chaîne et renvoyant une liste des ... choses qui en résultent. Faisons un faux départ:
(defun mess-with (number string)
'(value-of-number (1+ number) something-with-string (length string)))
Lisp> (mess-with 20 "foo")
(VALUE-OF-NUMBER (1+ NUMBER) SOMETHING-WITH-STRING (LENGTH STRING))
Hey! Ce n'est pas ce que nous voulions. Nous voulons évaluer de manière sélective certains arguments et laisser les autres comme symboles. Essayez # 2!
(defun mess-with (number string)
(list 'value-of-number (1+ number) 'something-with-string (length string)))
Lisp> (mess-with 20 "foo")
(VALUE-OF-NUMBER 21 SOMETHING-WITH-STRING 3)
Pas seulement quote
, maisbackquote
Bien mieux! Incidemment, ce modèle est si courant dans (principalement) les macros, qu'il existe une syntaxe spéciale pour faire exactement cela. Le backquote:
(defun mess-with (number string)
`(value-of-number ,(1+ number) something-with-string ,(length string)))
C'est comme utiliser quote
, mais avec la possibilité d'évaluer explicitement certains arguments en les préfixant par une virgule. Le résultat est équivalent à l'utilisation list
, mais si vous générez du code à partir d'une macro, vous ne voulez souvent évaluer que de petites parties du code renvoyé, donc le backquote est plus adapté. Pour les listes plus courtes, list
peut être plus lisible.
Hé, vous avez oublié quote
!
Alors, où cela nous laisse-t-il? Oh oui, qu'est-ce que ça quote
fait réellement? Il renvoie simplement son ou ses arguments non évalués! Vous vous souvenez de ce que j'ai dit au début à propos des fonctions régulières? Il s'avère que certains opérateurs / fonctions n'ont pas besoin d' évaluer leurs arguments. Comme IF - vous ne voudriez pas que la branche else soit évaluée si elle n'était pas prise, non? Les opérateurs dits spéciaux , ainsi que les macros, fonctionnent comme ça. Les opérateurs spéciaux sont également l '«axiome» du langage - ensemble minimal de règles - sur lequel vous pouvez implémenter le reste de Lisp en les combinant de différentes manières.
Retour à quote
, si:
Lisp> (quote spiffy-symbol)
SPIFFY-SYMBOL
Lisp> 'spiffy-symbol ; ' is just a shorthand ("reader macro"), as shown above
SPIFFY-SYMBOL
Comparez avec (sur Steel-Bank Common Lisp):
Lisp> spiffy-symbol
debugger invoked on a UNBOUND-VARIABLE in thread #<THREAD "initial thread" RUNNING {A69F6A9}>:
The variable SPIFFY-SYMBOL is unbound.
Type HELP for debugger help, or (SB-EXT:QUIT) to exit from SBCL.
restarts (invokable by number or by possibly-abbreviated name):
0: [ABORT] Exit debugger, returning to top level.
(SB-INT:SIMPLE-EVAL-IN-LEXENV SPIFFY-SYMBOL #<NULL-LEXENV>)
0]
Parce qu'il n'y spiffy-symbol
en a pas dans le périmètre actuel!
Résumer
quote
, backquote
(avec virgule), et list
sont quelques-uns des outils que vous utilisez pour créer des listes, qui ne sont pas seulement des listes de valeurs, mais, comme vous l'avez vu, peuvent être utilisées comme struct
des structures de données légères (pas besoin de définir a )!
Si vous souhaitez en savoir plus, je recommande le livre de Peter Seibel, Practical Common Lisp, pour une approche pratique de l'apprentissage de Lisp, si vous êtes déjà dans la programmation en général. Finalement, lors de votre voyage Lisp, vous commencerez également à utiliser des packages. The Idiot's Guide to Common Lisp Packages de Ron Garret vous en donnera une bonne explication.
Bon piratage!