Quand utiliser '(ou quote) en Lisp?

114

Après avoir parcouru les principales parties d'un livre d'introduction Lisp, je ne pouvais toujours pas comprendre ce que fait la fonction d'opérateur spécial (quote)(ou équivalent '), mais cela a été partout dans le code Lisp que j'ai vu.

Qu'est ce que ça fait?

Cristián Romo
la source

Réponses:

178

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 aet 2. La valeur du symbole aest recherchée dans le jeu de liaisons de variable courant, puis remplacée. Say aest 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 quotedé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à quotequ'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 allocateet freesont 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, listpeut être plus lisible.

Hé, vous avez oublié quote!

Alors, où cela nous laisse-t-il? Oh oui, qu'est-ce que ça quotefait 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-symbolen a pas dans le périmètre actuel!

Résumer

quote, backquote(avec virgule), et listsont 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 structdes 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!

Mikael Jansson
la source
Dans mon emacs SBCL est configuré et quand je tape `` 'this' is 'true `Il ne retourne que le dernier, c'est-à-dire TRUE en sortie. Même dans le portacle, j'obtiens la même sortie
Totoro
@Totoro La valeur de retour d'une fonction ou simplement de plusieurs instructions dans lisp est la dernière expression, donc elle retourne réellement this, alors is, alors true, mais vous ne voyez que la dernière renvoyée. (ceci est et vrai sont des déclarations séparées)
Wezl
52

Il dit "ne m'évaluez pas". Par exemple, si vous vouliez utiliser une liste en tant que données et non en tant que code, vous mettriez une citation devant celle-ci. Par exemple,

(print '(+ 3 4))imprime "(+ 3 4)", alors que (print (+ 3 4))imprime "7"

Adam Rosenfield
la source
Comment l'évaluer alors par exemple y a-t-il une unquotecommande?
Lime
3
@William Lisps ont une fonction pratique appelée eval: (print (eval '(+ 3 4))). C'est ce qui rend Lisps si génial: les listes sont du code, et le code est des listes, donc un programme Lisp peut se manipuler.
darkfeline
18

D'autres personnes ont répondu admirablement à cette question, et Matthias Benkard émet un excellent avertissement.

N'UTILISEZ PAS DE DEVIS POUR CRÉER DES LISTES QUE VOUS MODIFIEREZ PLUS TARD. La spécification permet au compilateur de traiter les listes citées comme des constantes. Souvent, un compilateur optimisera les constantes en leur créant une valeur unique en mémoire, puis en référençant cette valeur unique à partir de tous les emplacements où la constante apparaît. En d'autres termes, il peut traiter la constante comme une variable globale anonyme.

Cela peut causer des problèmes évidents. Si vous modifiez une constante, cela peut très bien modifier d'autres utilisations de la même constante dans un code totalement indépendant. Par exemple, vous pouvez comparer une variable à '(1 1) dans une fonction, et dans une fonction complètement différente, commencer une liste avec' (1 1) puis y ajouter d'autres éléments. En exécutant ces fonctions, vous pouvez constater que la première fonction ne correspond plus correctement aux choses, car elle essaie maintenant de comparer la variable à '(1 1 2 3 5 8 13), ce que la deuxième fonction a renvoyé. Ces deux fonctions sont totalement indépendantes, mais elles ont un effet l'une sur l'autre en raison de l'utilisation de constantes. Des effets encore plus fous peuvent se produire, comme une itération de liste parfaitement normale, soudainement en boucle infinie.

Utilisez quote lorsque vous avez besoin d'une liste de constantes, par exemple à des fins de comparaison. Utilisez la liste lorsque vous modifiez le résultat.

Xanthir
la source
Il semble donc que vous devriez utiliser la (list (+ 1 2)) plupart du temps. Si oui, comment empêcher l'évaluation de l' (+ 1 2)intérieur d'un tel exemple? Y a-t-il une unquotecommande?
Lime
1
Voulez-vous l'équivalent '((3))ou l'équivalent de '((+ 1 2))? Dans ce dernier cas, vous devez utiliser plus list: (list (list '+ 1 2)). Ou si vous vouliez l'équivalent de '(+ 1 2), juste (list '+ 1 2). Et rappelez-vous, si vous ne modifiez pas la liste, n'hésitez pas à utiliser la citation: rien de mal '(+ 1 2)si vous comparez simplement avec elle ou quelque chose.
Xanthir
1
Cela vous dérange-t-il de mentionner où les listes citées sont censées être traitées comme des constantes?
Lime
HyperSpec clhs.lisp.se/Body/s_quote.htm indique que le comportement n'est pas défini si l'objet cité est modifié de manière destructive. Cela implique que cela permet à impls de traiter les valeurs comme des valeurs atomiques.
Xanthir
14

Une réponse à cette question dit que QUOTE «crée des structures de données de liste». Ce n'est pas tout à fait vrai. QUOTE est plus fondamental que cela. En fait, QUOTE est un opérateur trivial: son but est d' empêcher que quoi que ce soit ne se passe. En particulier, cela ne crée rien.

Ce que (QUOTE X) dit, c'est "ne faites rien, donnez-moi simplement X". X n'a ​​pas besoin d'être une liste comme dans (QUOTE (ABC)) ou un symbole comme dans (QUOTE FOO). Cela peut être n'importe quel objet. En effet, le résultat de l'évaluation de la liste produite par (LIST 'QUOTE SOME-OBJECT) retournera toujours SOME-OBJECT, quel qu'il soit.

Maintenant, la raison pour laquelle (QUOTE (ABC)) semble avoir créé une liste dont les éléments sont A, B et C est qu'une telle liste est vraiment ce qu'elle renvoie; mais au moment où le formulaire QUOTE est évalué, la liste existe généralement déjà depuis un certain temps (en tant que composant du formulaire QUOTE!), créée soit par le chargeur, soit par le lecteur avant l'exécution du code.

Une implication de cela qui a tendance à trébucher assez souvent les débutants est qu'il est très imprudent de modifier une liste retournée par un formulaire QUOTE. Les données renvoyées par QUOTE doivent , à toutes fins utiles, être considérées comme faisant partie du code en cours d'exécution et doivent donc être traitées en lecture seule!

Matthias Benkard
la source
11

Le devis empêche l'exécution ou l'évaluation d'un formulaire, le transformant à la place en données. En général, vous pouvez exécuter les données en les évaluant ensuite.

quote crée des structures de données de liste, par exemple, les éléments suivants sont équivalents:

(quote a)
'a

Il peut également être utilisé pour créer des listes (ou des arbres):

(quote (1 2 3))
'(1 2 3)

Vous feriez probablement mieux de vous procurer un livre d'introduction sur lisp, tel que Practical Common Lisp (qui peut être lu en ligne).

Kyle Burton
la source
3

Dans Emacs Lisp:

Que peut-on citer?

Listes et symboles.

Citer un nombre équivaut au nombre lui-même: '5c'est la même chose que5 .

Que se passe-t-il lorsque vous citez des listes?

Par exemple:

'(one two) évalue à

(list 'one 'two) qui évalue à

(list (intern "one") (intern ("two"))).

(intern "one")crée un symbole nommé "un" et le stocke dans une table de hachage "centrale", donc à chaque fois que vous dites 'onele symbole nommé"one" sera recherché dans cette carte de hachage centrale.

Mais qu'est-ce qu'un symbole?

Par exemple, dans les langages OO (Java / Javascript / Python), un symbole pourrait être représenté comme un objet qui a un namechamp, qui est le nom du symbole comme"one" ci-dessus, et des données et / ou du code peuvent lui être associés à cet objet.

Ainsi, un symbole en Python pourrait être implémenté comme:

class Symbol:
   def __init__(self,name,code,value):
       self.name=name
       self.code=code
       self.value=value

Dans Emacs Lisp par exemple, un symbole peut avoir 1) des données qui lui sont associées ET (en même temps - pour le même symbole) 2) un code qui lui est associé - selon le contexte, les données ou le code sont appelés.

Par exemple, dans Elisp:

(progn
  (fset 'add '+ )
  (set 'add 2)
  (add add add)
)

évalue à 4.

Parce que (add add add)s'évalue comme:

(add add add)
(+ add add)
(+ 2 add)
(+ 2 2)
4

Ainsi, par exemple, en utilisant la Symbolclasse que nous avons définie en Python ci-dessus, ce addsymbole ELisp pourrait être écrit en Python sous la forme Symbol("add",(lambda x,y: x+y),2).

Merci beaucoup aux gens sur IRC #emacs pour m'avoir expliqué les symboles et les citations.

jhegedus
la source
2

Lorsque nous voulons passer un argument lui-même au lieu de passer la valeur de l'argument, nous utilisons quote. Il est principalement lié à la procédure passant lors de l'utilisation de listes, de paires et d'atomes qui ne sont pas disponibles en langage de programmation C (la plupart des gens commencent à programmer en utilisant la programmation C, nous sommes donc confus) Ceci est un code en langage de programmation Scheme qui est un dialecte de lisp et je suppose que vous pouvez comprendre ce code.

(define atom?              ; defining a procedure atom?
  (lambda (x)              ; which as one argument x
(and (not (null? x)) (not(pair? x) )))) ; checks if the argument is atom or not
(atom? '(a b c)) ; since it is a list it is false #f

La dernière ligne (atom? 'Abc) passe abc tel quel à la procédure pour vérifier si abc est un atome ou non, mais lorsque vous passez (atom? Abc) alors il vérifie la valeur de abc et passe la valeur à il. Depuis, nous ne lui avons fourni aucune valeur

erreur inconnue
la source
1

Quote renvoie la représentation interne de ses arguments. Après avoir parcouru beaucoup trop d'explications sur ce que la citation ne fait pas , c'est alors que l'ampoule s'est allumée. Si le REPL n'a pas converti les noms de fonction en UPPER-CASE lorsque je les ai cités, cela ne m'est peut-être pas venu à l'esprit.

Alors. Les fonctions Lisp ordinaires convertissent leurs arguments en une représentation interne, évaluent les arguments et appliquent la fonction. Quote convertit ses arguments en une représentation interne et la renvoie simplement. Techniquement, il est correct de dire que la citation dit «ne pas évaluer», mais lorsque j'essayais de comprendre ce que cela faisait, me dire ce que cela ne faisait pas était frustrant. Mon grille-pain n'évalue pas non plus les fonctions Lisp; mais ce n'est pas ainsi que vous expliquez ce que fait un grille-pain.

Steve
la source
1

Une réponse courte:

quotesignifie sans l'évaluer, et le backquote est un devis mais laissez les portes dérobées .

Une bonne référence:

Le manuel de référence Emacs Lisp le rend très clair

9.3 Citations

Le formulaire spécial quote renvoie son argument unique, tel qu'il est écrit, sans l'évaluer. Cela permet d'inclure des symboles et des listes de constantes, qui ne sont pas des objets auto-évalués, dans un programme. (Il n'est pas nécessaire de citer des objets auto-évalués tels que des nombres, des chaînes et des vecteurs.)

Formulaire spécial: objet de devis

This special form returns object, without evaluating it. 

Parce que quote est si souvent utilisée dans les programmes, Lisp fournit une syntaxe de lecture pratique. Un caractère apostrophe ('' ') suivi d'un objet Lisp (en syntaxe de lecture) se développe en une liste dont le premier élément est quote, et dont le deuxième élément est l'objet. Ainsi, la syntaxe de lecture 'x est une abréviation pour (quote x).

Voici quelques exemples d'expressions utilisant quote:

(quote (+ 1 2))
      (+ 1 2)

(quote foo)
      foo

'foo
      foo

''foo
      (quote foo)

'(quote foo)
      (quote foo)

9.4 Contexte

Les constructions de backquote vous permettent de citer une liste, mais d'évaluer de manière sélective les éléments de cette liste. Dans le cas le plus simple, il est identique au devis de formulaire spécial (décrit dans la section précédente; voir Citations). Par exemple, ces deux formulaires donnent des résultats identiques:

`(a list of (+ 2 3) elements)
      (a list of (+ 2 3) elements)

'(a list of (+ 2 3) elements)
      (a list of (+ 2 3) elements)

Le marqueur spécial ',' à l'intérieur de l'argument de backquote indique une valeur qui n'est pas constante. L'évaluateur Emacs Lisp évalue l'argument de ',' et place la valeur dans la structure de la liste:

`(a list of ,(+ 2 3) elements)
      (a list of 5 elements)

La substitution par «,» est également autorisée à des niveaux plus profonds de la structure de la liste. Par exemple:

`(1 2 (3 ,(+ 4 5)))
      (1 2 (3 9))

Vous pouvez également épisser une valeur évaluée dans la liste résultante, en utilisant le marqueur spécial ', @'. Les éléments de la liste épissée deviennent des éléments au même niveau que les autres éléments de la liste résultante. Le code équivalent sans utiliser '' 'est souvent illisible. Voici quelques exemples:

(setq some-list '(2 3))
      (2 3)

(cons 1 (append some-list '(4) some-list))
      (1 2 3 4 2 3)

`(1 ,@some-list 4 ,@some-list)
      (1 2 3 4 2 3)
Andrew_1510
la source
1
Code is data and data is code.  There is no clear distinction between them.

C'est une déclaration classique que tout programmeur lisp connaît.

Lorsque vous citez un code, ce code sera des données.

1 ]=> '(+ 2 3 4)
;Value: (+ 2 3 4)

1 ]=> (+ 2 3 4)
;Value: 9

Lorsque vous citez un code, le résultat sera des données qui représentent ce code. Ainsi, lorsque vous souhaitez travailler avec des données qui représentent un programme, vous citez ce programme. Ceci est également valable pour les expressions atomiques, pas seulement pour les listes:

1 ]=> 'code
;Value: code

1 ]=> '10
;Value: 10

1 ]=> '"ok"
;Value: "ok"

1 ]=> code
;Unbound variable: code

Supposons que vous vouliez créer un langage de programmation intégré dans lisp - vous travaillerez avec des programmes qui sont cités dans le schéma (comme '(+ 2 3)) et qui sont interprétés comme du code dans le langage que vous créez, en donnant aux programmes une interprétation sémantique. Dans ce cas, vous devez utiliser quote pour conserver les données, sinon elles seront évaluées dans un langage externe.

Alinsoar
la source