52 votes

Comment vivre avec Emacs Lisp dynamique de portée?

J'ai appris Clojure précédemment et aime vraiment la langue. J'aime aussi Emacs et ont piraté certaines choses simples avec Emacs Lisp. Il y a une chose qui m'empêche mentalement de faire quelque chose de plus substantiel avec Elisp bien. C'est le concept de dynamique de portée. Je suis juste peur de lui car il est tellement étranger à moi et l'odeur des semi-variables globales.

Donc, avec les déclarations de variables, je ne sais pas laquelle les choses sont sans danger et qui sont dangereux. De ce que j'ai compris, les variables définies avec setq relèvent de la dynamique de la portée (c'est bien cela?) Ce sujet de laisser des variables? Quelque part j'ai lu que laissez vous permet de faire des plaine portée lexicale, mais quelque part j'ai lu que laisser vars sont également de façon dynamique étendue.

Je quess mon plus gros souci est que mon code (à l'aide de setq ou laisser) casse accidentellement certaines variables de la plate-forme ou de la troisième partie du code que j'appelle ou que, après un tel appel mes variables locales sont foiré accidentellement. Comment puis-je éviter cela?

Il y a quelques règles simples que je peux il suffit de suivre et de savoir exactement ce qui se passe avec le champ d'application sans être mordu dans certains bizarre, difficile à déboguer façon?

45voto

Rainer Joswig Points 62532

Il n'est pas mauvais.

Les principaux problèmes peuvent apparaître avec "libre" variables dans les fonctions.

(defun foo (a)
  (* a b))

En fonction ci-dessus a est une variable locale. b est une variable libre. Dans un système de liaison dynamique comme Emacs Lisp, b va être regardé lors de l'exécution. Il y a maintenant trois cas:

  1. b n'est pas défini -> erreur
  2. b est une variable locale lié par certaines appel de fonction dans le courant de la dynamique de la portée -> prendre de la valeur
  3. b est une variable globale -> prendre de la valeur

Les problèmes peuvent alors être:

  • une valeur limite (global ou local) est masqué par un appel de fonction, éventuellement indésirables
  • une variable non définie n'est PAS l'ombre -> erreur sur l'accès
  • une variable globale n'est PAS l'ombre -> récupère la valeur globale, qui peut être indésirable

Dans un Lisp avec un compilateur, de la compilation de la fonction ci-dessus peut générer un avertissement qu'il y est une variable libre. Généralement Common Lisp compilateurs faire. Un interprète ne fournira pas de cet avertissement, on va juste voir l'effet au moment de l'exécution.

Conseils:

  • assurez-vous que vous n'utilisez pas de variables libres accidentellement
  • assurez-vous que les variables globales ont un nom spécial, de sorte qu'ils sont faciles à repérer dans le code source, habituellement *foo-var*

Ne pas écrire

(defun foo (a b)
   ...
   (setq c (* a b))  ; where c is a free variable
   ...)

Écrire:

(defun foo (a b)
   ...
   (let ((c (* a b)))
     ...)
   ...)

Lier toutes les variables que vous souhaitez utiliser et vous voulez vous assurer qu'ils ne sont pas liés à un autre endroit.

C'est principalement ça.

Depuis GNU Emacs version 24 lexicale de liaison est prise en charge dans sa Emacs Lisp. Voir: Lexical de Liaison, GNU Emacs Lisp Manuel de Référence.

13voto

Jérôme Radix Points 4691

Il y a quelques règles simples que je peux il suffit de suivre et de savoir exactement ce qui se passe avec le champ d'application sans être mordu dans certains bizarre, difficile à déboguer façon?

Lire Emacs Lisp de Référence, vous aurez beaucoup de détails comme celui-ci :

  • Forme particulière: setq [symbole de la forme]... Cette forme particulière est la méthode la plus commune de la modification d'un valeur de la variable. Chaque SYMBOLE est donné une nouvelle valeur, qui est la résultat de l'évaluation du FORMULAIRE correspondant. Les plus-local existant de liaison du symbole est modifiée.

Voici un exemple :

(defun foo () (setq tata "foo"))

(defun bar (tata) (setq tata "bar"))


(foo)
(message tata)
    ===> "foo"


(bar tata)
(message tata)
    ===> "foo"

13voto

danlei Points 8602

En outre, le dernier alinéa de l'Gilles réponse, voici comment RMS plaide en faveur de la dynamique de la portée dans un système extensible:

Certaines langues les concepteurs estiment que liaison dynamique doit être évitée, et argument explicite passage devrait être utilisé à la place. Imaginez que la fonction d'Un lie la variable FOO, et appelle la la fonction B, qui appelle la fonction En C et en C utilise la valeur de TOTO. Le soi-disant doit passer la valeur de l' un argument de B, qui devrait passer comme un argument de C.

Cela ne peut être fait dans un extensible système, cependant, parce que l'auteur de le système ne peut pas savoir ce qu'il en les paramètres seront. Imaginez que l' les fonctions A et C font partie d'un utilisateur extension, tandis que B est une partie de la système standard. La variable FOO n' n'existe pas dans le système standard; il fait partie de l'extension. Pour utiliser argument explicite passaient, nécessitent l'ajout d'un nouvel argument à B, ce qui signifie réécriture B et tout qui appelle B. Dans le cas le plus fréquent, B est la commande de l'éditeur de répartiteur boucle, qui est appelée à partir d'une terrible nombre de places.

Ce qui est pire, C doit également être transmis à un argument supplémentaire. B n'a rien à voir de C par nom (C n'existe pas quand B a été écrit). Il trouve probablement un pointeur vers C dans le commandement de l'expédition table. Cela signifie que le même appel qui appelle parfois C peut également appeler n'importe quel éditeur de commande définition. Donc, tout le montage les commandes doivent être réécrites pour accepter et d'ignorer l'argument supplémentaire. Par maintenant, aucun système d'origine est gauche!

Personnellement, je pense que si il y a un problème avec Emacs-Lisp, il n'est pas dynamique portée en soi, mais que c'est la valeur par défaut, et qu'il n'est pas possible d'atteindre une portée lexicale sans avoir recours à des extensions. En CL, à la fois dynamique et portée lexicale peut être utilisé, sauf pour le niveau le plus haut (qui est adressée par plusieurs deflex-implémentations) et déclarés globalement variables spéciales -- la valeur par défaut est portée lexicale. En Clojure, trop, vous pouvez utiliser à la fois lexicale et dynamique de la portée.

Pour citer RMS de nouveau:

Il n'est pas nécessaire pour la dynamique de la portée pour être le seul champ d'application de la règle prévue, juste utile pour qu'il soit disponible.

11voto

Gizmomogwai Points 886

Comme Peter Ajtai a souligné:

Depuis emacs-24.1 vous pouvez activer la portée lexicale fichier par fichier en mettant

;; -*- lexical-binding: t -*-

sur le dessus de votre elisp fichier.

10voto

Vatine Points 8884

Tout d'abord, elisp a séparé les variables et fonctions de liaisons, de sorte que certains des pièges de la dynamique de la portée ne sont pas pertinents.

Deuxièmement, vous pouvez toujours utiliser setq de définir des variables, mais la valeur ne survivra pas à la sortie de la dynamique de la portée c'est fait. Ce n'est pas fondamentalement différente de portée lexicale, avec la différence qu'avec la dynamique de la portée d'un setq dans une fonction que vous appelez peut affecter la valeur de vous voir après l'appel de la fonction.

Il y a lexical-let, une macro (essentiellement) imite lexicale liaisons (je crois qu'il le fait en marche le corps et changer toutes les occurrences de la lexicalement laisser des variables à un gensymmed nom, finalement uninterning le symbole), si vous avez absolument besoin.

Je dirais "écrire du code comme d'habitude". Il ya des moments où la nature dynamique de elisp va vous mordre, mais j'ai trouvé que, dans la pratique, qui est étonnamment rarement.

Voici un exemple de ce que je disais à propos de setq et dynamiquement les variables liées (récemment évalué à proximité, dans un zéro de la mémoire tampon):

(let ((a nil))
  (list (let ((a nil))
          (setq a 'value)
          a)
        a))

(value nil)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X