Comment créer une fonction temporaire dans Emacs Lisp

Je fais des appels fastidieux à un tas de fonctions, mais les paramètres seront déterminés à l'exécution. J'ai écrit une fonction simple pour garder mon code sec mais lui donner un nom est inutile. Je n'utilise cette fonction nulle part ailleurs.

J'essaie de le faire comme je le ferais dans Scheme, mais j'obtiens une erreur void-function:

(let ((do-work (lambda (x y z)
                  (do-x x)
                  (do-y y)
                  ;; etc
                  )))
  (cond (test-1 (do-work 'a 'b 'c))
        (test-2 (do-work 'i 'j 'k))))

Je pourrais tout coller dans un apply (par exemple, (apply (lambda ...) (cond ...))) mais ce n'est pas très lisible. Est-il un meilleur moyen?

23
demandé sur Cristian 2010-04-17 09:10:20

3 réponses

Comme les autres Lisp (mais pas Scheme), Emacs Lisp a des espaces de noms distincts pour les variables et les fonctions (c'est-à-dire un ‘Lisp2’, pas un " Lisp1; Voir questions techniques de séparation dans les cellules de fonction et les cellules de valeur pour l'origine et la signification de ces termes).

Vous devrez utiliser funcall ou apply pour appeler un lambda (ou une autre fonction) stocké dans une variable.

(cond (test-1 (funcall do-work 'a 'b 'c))
      (test-2 (funcall do-work 'i 'j 'k))

Utiliser funcall si vous envoyez toujours le même nombre de argument. Utilisez apply Si vous devez pouvoir envoyer un nombre variable d'arguments.

Le mécanisme interne est que chaque symbole a plusieurs "cellules". Quelle cellule est utilisée dépend de où le symbole est sous une forme évaluée . Lorsqu'un symbole est le premier élément d'une forme évaluée, sa cellule "fonction" est utilisée. Dans toute autre position, sa cellule" valeur " est utilisée . Dans votre code, do-work a la fonction dans sa cellule de valeur. Pour y accéder en tant que la fonction que vous utilisez funcall ou apply. S'il était dans la cellule de fonction, vous pourriez l'appeler directement en utilisant son nom comme voiture d'un formulaire évalué. Vous pouvez accomplir cela avec flet ou labels de la cl package.

28
répondu Chris Johnsen 2010-04-17 07:04:28

Faites (require 'cl) pour extraire le paquet Common Lisp, puis utilisez flet au lieu de let:

(flet ((do-work (x y z)
          (do-x x)
          (do-y y)
          ;; etc
          ))
  (cond (test-1 (do-work 'a 'b 'c))
        (test-2 (do-work 'i 'j 'k))))
21
répondu Sean 2010-04-17 05:29:42

Vous pouvez le faire de la manière ANSI Common Lisp (bien que je pense qu'il y a des développements Emacs qui vous donneront des regards méchants):

(flet ((do-work (x y z)
                (do-x x)
                (do-y y)
                ;; etc
                ))
  (cond (test-1 (do-work 'a 'b 'c))
        (test-2 (do-work 'i 'j 'k))))

Je ne sais pas si vous serez d'abord (require 'cl) (ou cl-macs?) pour utiliser flet. Si vous voulez définir des fonctions récursives, vous devrez utiliser labels IIRC.

4
répondu draebek 2010-04-17 05:34:45