Quand citer une expression lambda avec précision?


30

Q: Quand, si jamais, est-il utile de citer un aiguisé lambda, et quand, si jamais, ne faut-il pas citer un a lambda?

Les gens utilisent lambdas de trois manières:

  1. plaine: (lambda (x) x)
  2. cité: '(lambda (x) x)
  3. pointu: #'(lambda (x) x)

Ce thread SO discute des trois types, ce thread SO explique pourquoi ne pas citer (NB: pas de guillemets pointus ) lambda, et ce thread SO discute également des distinctions entre les citations et les citations pointues.

Maintenant, le nœud manuel sur les fonctions anonymes et la docstring pour lambdanoter que les lambdas sont auto-cités:

Un appel du formulaire (lambda ARGS DOCSTRING INTERACTIVE BODY)est auto-citant; le résultat de l'évaluation de l'expression lambda est l'expression elle-même. L'expression lambda peut alors être traitée comme une fonction ...

Ainsi, il semble que (lambda (x) x)et #'(lambda (x) x)sont équivalents, mais '(lambda (x) x)ne le sont pas (surtout, lors de la compilation d'octets).

Il semble que l'on veuille rarement citer un lambda, mais je ne sais pas quand, si jamais, nous devrions, ou ne devrions pas, citer clairement:

  • La citation nette est-elle lambdasimplement un choix stylistique ou existe-t-il des circonstances dans lesquelles la citation nette est réellement utile?
  • Y a-t-il des circonstances dans lesquelles nous ne devons pas citer de façon précise a lambda, c'est-à-dire lorsque cela changerait le sens du code?

Réponses:


28

Il était une fois , la citation pointue était nécessaire pour les lambdas, maintenant ce n'est plus le cas.

Ainsi, il semble que (lambda (x) x) et # '(lambda (x) x) sont équivalents, mais' (lambda (x) x) ne l'est pas (surtout, lors de la compilation d'octets).

Oui. En fait, les deux premiers sont complètement identiques lorsqu'ils sont évalués. Comme décrit dans la page de manuel que vous avez liée:

Les formulaires suivants sont tous équivalents:

(lambda (x) (* x x)) 
(function (lambda (x) (* x x))) 
#'(lambda (x) (* x x))

À part essayer de prendre en charge les versions d'Emacs d'il y a deux décennies, il n'y a jamais de raison de citer un lambda.

Alors ne le fais pas.


En tant que sidenote:

  • Le fait de citer un lambda (avec ') fait une différence, il empêche la compilation d'octets. Je ne peux pas penser à un scénario où c'est utile, mais qui sait.

  • Le backtic est la seule citation qui soit vraiment utile avec les lambdas, mais seulement si vous n'utilisez pas de reliure lexicale pour une raison quelconque.


Pensez à ajouter un lien vers la section Fonctions anonymes du manuel qui contient un exemple expliquant l'effet de la citation sur la compilation d'octets.
Constantine

@Constantine Done. Je suis paresseux parce que je suis au téléphone, et l'OP l'a déjà lié de toute façon.
Malabarba

Pouvez-vous clarifier ce que vous entendez par ne pas utiliser de reliure lexicale et de backtick? Merci.
coredump du

@coredump Avec la liaison dynamique, la seule façon de rendre les variables externes accessibles à l'intérieur d'un lambda est de construire manuellement le lambda sous forme de liste avec la variable à l'intérieur. Les backtics sont bons pour ce genre de chose.
Malabarba du

BTW, je ne pense pas que "il était une fois" s'applique vraiment: quand j'ai étudié ce sujet dans l'historique des révisions, j'ai trouvé qu'il lambdaa été défini comme une macro qui ajoute le functionfor aussi loin que possible. Si l'OIE #'a été nécessaire à un moment donné, c'était dans le code de développement très précoce. Il n'était certainement pas nécessaire déjà dans Emacs-18.
Stefan

5

Puisque lambdane fait pas de sens quand il est pas cité, les versions récentes de suivi Emacs Lisp (ANSI) Common Lisp dans l' interprétation non cotées (lambda...)comme #'(lambda...). Les deux notations sont presque exactement équivalentes (sauf dans la structure citée).

Que ce soit à préférer (lambda...)ou #'(lambda...)est donc purement une question de style. Certaines personnes préfèrent la forme nue, ce qui évite le bruit syntaxique, tandis que d'autres (y compris moi-même) préfèrent la forme citée.


Cela contredit le manuel elisp: "Dans Emacs Lisp, une telle liste est une expression valide qui s'évalue en un objet fonction."
djechlin

8
"Versions récentes" comme dans "versions publiées après 1990 environ" ;-)
Stefan

0

Ajouter un peu d'histoire supplémentaire, en raison de voir l' héritage historique de # '(lambda ...)?

https://debbugs.gnu.org/cgi/bugreport.cgi?bug=4290 suggère que:

À partir d'Emacs 22, un lambdaformulaire est compilé en octets lorsqu'il est utilisé en tant que fonction, qu'il soit précédé de functionou #'. Avec les versions d'Emacs antérieures à 22, vous devez utiliser explicitement #' ou functionsi vous souhaitez que le formulaire soit compilé en octets.

Je ne connais pas le compilateur d'octets, mais je peux voir qu'au moins dès 1993, la lambdamacro elle-même a renvoyé un (function (lambda ...))formulaire.

https://www.iro.umontreal.ca/~monnier/hopl-4-emacs-lisp.pdf dit également:

Fait intéressant (contrairement à MacLisp), lambdail ne faisait techniquement pas partie du langage Elisp avant 1991 environ lorsqu'il a été ajouté en tant que macro, au début du développement d'Emacs-19. Dans Emacs-18, les fonctions anonymes étaient écrites sous forme de valeurs entre guillemets du formulaire:

'(lambda (..ARGS..) ..BODY..)

Bien que la lambdamacro ait rendu cette citation inutile depuis près de 30 ans maintenant, de nombreux cas de cette pratique se produisent toujours dans le code Elisp, même si cela empêche la compilation d'octets du corps. De manière relativement similaire, ce n'est qu'en 1993 que Lucid Emacs 19.8 a importé le #'...raccourci du lecteur pour (function ...)de MacLisp. Emacs a emboîté le pas l'année suivante.


0

Je veux juste donner un exemple pratique d'utilisation de l'expression lambda réticulaire. Il s'agit de liaison lexicale / de l'observation de variables, l'utilisation d'une expression lambda rétroactive et le référencement de variables avec une virgule permettent d'obtenir leur valeur globale.

;; -*- lexical-binding:t -*-
(let ((my-variable "Random old"))
  (funcall `(lambda()
             (let ((my-variable "Random"))
               (message ,my-variable)))))

M-x [RET] eval-buffer sorties "Random old"

;; -*- lexical-binding:t -*-
(let ((my-variable "Random old"))
  (funcall (lambda()
             (let ((my-variable "Random"))
               (message my-variable)))))

M-x [RET] eval-buffer sorties "aléatoire"

Un troisième exemple combinant variable globale et variable locale de variable

;; -*- lexical-binding:t -*-
(let ((my-variable "Random old"))
  (funcall `(lambda()
              (let ((my-variable "Random"))
                (message my-variable)
                (message ,my-variable)))))

M-x [RET] eval-buffer sorties "Random" "Random old"


@npostavs ce n'était pas le point avec mon exemple mais j'ai aussi modifié mon exemple pour éviter cette mauvaise pratique
cjohansson

Mieux, même si je ne suis toujours pas convaincu qu'il s'agit d'une amélioration par rapport au simple choix d'un nom différent pour la reliure intérieure.
npostavs le
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.