Question

Je suis en train de parcourir le livre de Graham "On Lisp" et je n'arrive pas à comprendre l'exemple suivant à la page 37 :

If we define exclaim so that its return value
incorporates a quoted list,

(defun exclaim (expression)
  (append expression ’(oh my)))

>  (exclaim ’(lions and tigers and bears))
(LIONS AND TIGERS AND BEARS OH MY)
> (nconc * ’(goodness))
(LIONS AND TIGERS AND BEARS OH MY GOODNESS)

could alter the list within the function:

> (exclaim ’(fixnums and bignums and floats))
(FIXNUMS AND BIGNUMS AND FLOATS OH MY GOODNESS)

To make exclaim proof against such problems, it should be written:
(defun exclaim (expression)
  (append expression (list ’oh ’my)))

Est-ce que quelqu'un comprend ce qui se passe ici ?Cela dérange sérieusement mon modèle mental de ce que fait la citation.

Était-ce utile?

La solution

nconc est une opération destructrice qui modifie son premier argument en changeant sa queue. Dans ce cas, cela signifie que la liste constante '(oh my) obtient une nouvelle queue.

Pour nous espérons que cela plus clair. Il est un peu comme ceci:

; Hidden variable inside exclaim
oh_my = oh → my → nil

(exclaim '(lions and tigers and bears)) =
    lions → and → tigers → and → bears → oh_my

(nconc * '(goodness)) destructively appends goodness to the last result:
    lions → and → tigers → and → bears → oh → my → goodness → nil
so now, oh_my = oh → my → goodness → nil

Remplacement '(oh my) avec des corrections de (list 'oh 'my) car il n'y a plus un être constant partagé par tout le monde. Chaque appel à exclaim génère une nouvelle liste (la fin de la fonction de list dans la vie est de créer de nouvelles listes de marque).

Autres conseils

L’observation selon laquelle votre modèle mental de citation peut être défectueux est une excellente observation, même si elle peut ou non s’appliquer en fonction de ce modèle mental.

Tout d’abord, rappelez-vous que l’exécution d’un programme comporte différentes étapes.Un environnement Lisp doit d'abord lire le texte du programme en structures de données (listes, symboles et diverses données littérales telles que des chaînes et des nombres).Ensuite, cela peut ou non compiler ces structures de données en code machine ou dans une sorte de format intermédiaire.Finalement, le code résultant est évalué (dans le cas du code machine, bien sûr, cela peut simplement signifier sauter à l'adresse appropriée).

Laissons de côté pour l'instant la question de la compilation et concentrons-nous sur les étapes de lecture et d'évaluation, en supposant (pour simplifier) ​​que l'entrée de l'évaluateur est la liste des structures de données lues par le lecteur.

Considérez un formulaire (QUOTE x)X est une représentation textuelle d'un objet.Cela peut être un symbole littéral comme dans (QUOTE ABC), une liste littérale comme dans (QUOTE (A B C)), une chaîne littérale comme dans (QUOTE "abc"), ou tout autre type de littéral.Au stade de la lecture, le lecteur lira le formulaire sous forme de liste (appelez-le formulaire 1) dont le premier élément est le symbole QUOTE et dont le deuxième élément est l'objet X' dont la représentation textuelle est X.Notez que je dis spécifiquement que l'objet X' est stocké dans la liste qui représente l'expression, c'est à dire.dans un sens, il est stocké sous forme une partie du code lui-même.

C'est maintenant au tour de l'évaluateur.La contribution de l'évaluateur est formulaire 1, qui est une liste.Il examine donc le premier élément de formulaire 1, et, après avoir déterminé que c'est le symbole QUOTE, il renvoie comme résultat de l'évaluation le deuxième élément de la liste.C’est le point crucial.L'évaluateur renvoie le deuxième élément de la liste à évaluer, qui est ce que le lecteur a lu lors de la première étape d'exécution (avant la compilation !). C'est tout ce qu'il fait. Il n'y a pas de magie, c'est très simple et, de manière significative, aucun nouvel objet n'est créé et aucun objet existant n'est copié.

Par conséquent, chaque fois que vous modifiez une « liste citée », vous modifiez le code lui-même.Le code auto-modifiable est une chose très déroutante, et dans ce cas, le comportement est en réalité indéfini (car ANSI Common Lisp permet aux implémentations de mettre le code en mémoire morte).

Bien entendu, ce qui précède n’est qu’un modèle mental.Les implémentations sont libres d'implémenter le modèle de différentes manières, et en fait, je ne connais aucune implémentation de Common Lisp qui, comme mon explication, ne fait aucune compilation.Pourtant, c’est l’idée de base.

En Common Lisp.

Rappelez-vous:

'(1 2 3 4)

Au-dessus est une liste littérale . données constantes .

(list 1 2 3 4)

LIST est une fonction que lorsque l'appel retourne un nouveau frais Liste avec ses arguments comme éléments de la liste.

Évitez de modifier les littéraux. Les effets ne sont pas standardisés. Imaginez un Lisp qui compile toutes les données constantes en écriture seule zone de mémoire. Imaginez un Lisp qui prend des listes constantes et les partage entre les différentes fonctions.

(defun a () '(1 2 3)

(defun b () '(1 2 3))

Un compilateur Lisp peut créer une liste qui est partagée par les deux fonctions.

Si vous modifiez la liste renvoyée par la fonction d'un

  • il pourrait ne pas être modifié
  • il peut être modifié
  • il est peut-être une erreur
  • il peut aussi changer la liste renvoyée par la fonction b

Implémentations ont la liberté de faire ce qu'ils aiment. Cela laisse place à des optimisations.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top