Quando aspas nítidas uma expressão lambda?

30

P: Quando, se for o caso, é útil fazer aspas a a lambdae quando, se for o caso, não devemos citar a a lambda?

As pessoas usam lambdas de três maneiras:

  1. avião: (lambda (x) x)
  2. citado: '(lambda (x) x)
  3. citações nítidas: #'(lambda (x) x)

Esse thread SO discute os três tipos, esse thread SO explica por que não citar (NB: not -sharp-quote ) lambdas, e esse thread SO também discute as distinções entre aspas e aspas .

Agora, o nó manual em funções anônimas e a sequência de caracteres para lambdaobservar que lambdas são citados automaticamente:

Uma chamada do formulário (lambda ARGS DOCSTRING INTERACTIVE BODY)é citada automaticamente; o resultado da avaliação da expressão lambda é a própria expressão. A expressão lambda pode então ser tratada como uma função ...

Portanto, parece que (lambda (x) x)e #'(lambda (x) x)são equivalentes, mas '(lambda (x) x)não são (o mais importante, quando a compilação de bytes).

Parece que alguém raramente gostaria de citar a lambda, mas não está claro para mim quando, se é que deveríamos, deveríamos, ou não, citar:

  • A citação nítida é lambdasimplesmente uma escolha estilística ou há circunstâncias em que a citação nítida é realmente útil?
  • Existem circunstâncias em que devemos não sharp-citar um lambda, isto é, quando isso alteraria o significado do código?
Dan
fonte

Respostas:

28

Era uma vez , a citação precisa era necessária para as lambdas, agora esse não é mais o caso.

Portanto, parece que (lambda (x) x) e # '(lambda (x) x) são equivalentes, mas' (lambda (x) x) não é (o mais importante, na compilação de bytes).

Sim. De fato, os dois primeiros são completamente idênticos quando avaliados. Conforme descrito na página de manual que você vinculou:

Os seguintes formulários são todos equivalentes:

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

Além de tentar suportar as versões do Emacs de duas décadas atrás, nunca há motivo para citar um lambda com afinco.

Então não.


Como uma nota rodapé:

  • A citação de um lambda (com ') faz a diferença, evita a compilação de bytes. Não consigo pensar em um cenário em que isso seja útil, mas quem sabe.

  • O backtic é a única citação que é realmente útil para lambdas, mas apenas se você não estiver usando a ligação lexical por algum motivo.

Malabarba
fonte
Considere adicionar um link à seção Funções Anônimas do manual, que contém um exemplo que explica o efeito de citação na compilação de bytes.
Constantine
@Constantine Done. Fiquei preguiçoso porque estou em um telefone e o OP já o vinculou de qualquer maneira.
Malabarba
Você pode esclarecer o que você quer dizer com não usar encadernação lexical e backtick? Obrigado.
Coredump
@coredump Com ligação dinâmica, a única maneira de tornar variáveis ​​externas acessíveis dentro de um lambda é construindo manualmente o lambda como uma lista com a variável dentro. Backtics são bons para esse tipo de coisa.
Malabarba
Aliás, não acho que "era uma vez" realmente se aplique: quando investiguei esse assunto no histórico de revisões, descobri que lambdaisso foi definido como uma macro que adiciona o valor o functionmais antigo possível. IOW, se #'necessário em algum momento, estava no código de desenvolvimento muito cedo. Certamente já não era necessário no Emacs-18.
21417 Stefan #
5

Como lambdanão faz sentido quando não está entre aspas, as versões recentes do Emacs Lisp seguem o Common Lisp (ANSI) na interpretação entre aspas (lambda...)como #'(lambda...). As duas notações são quase exatamente equivalentes (exceto dentro da estrutura citada).

Se prefere (lambda...)ou #'(lambda...)é, portanto, puramente uma questão de estilo. Algumas pessoas preferem a forma nua, o que evita ruídos sintáticos, enquanto outras (inclusive eu) preferem a forma citada.

jch
fonte
Isso contradiz o manual elisp: "No Emacs Lisp, essa lista é uma expressão válida que é avaliada como um objeto de função".
djechlin
8
"As versões recentes", como em "versões lançado depois de 1990 ou assim" ;-)
Stefan
0

Adicionando um pouco de histórico adicional, por ter visto o legado histórico Is # '(lambda ...)?

https://debbugs.gnu.org/cgi/bugreport.cgi?bug=4290 sugere que:

A partir do Emacs 22, um lambdaformulário é compilado em bytes quando usado como uma função, independentemente de ser precedido por functionou #'. Nas versões do Emacs anteriores a 22, você deve usar explicitamente #' ou functionse desejar que o formulário seja compilado em bytes.

Não conheço o byte-compiler, mas vejo que, pelo menos em 1993, a lambdaprópria macro retornou um (function (lambda ...))formulário.

https://www.iro.umontreal.ca/~monnier/hopl-4-emacs-lisp.pdf também diz:

Curiosamente (ao contrário do MacLisp), lambdanão era tecnicamente parte da linguagem Elisp até por volta de 1991, quando foi adicionada como macro, no início do desenvolvimento do Emacs-19. No Emacs-18, funções anônimas foram escritas como valores citados no formulário:

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

Embora a lambdamacro tenha tornado essa citação desnecessária há quase 30 anos, muitas instâncias dessa prática ainda ocorrem no código Elisp, apesar de impedir a compilação de bytes do corpo. De alguma forma, apenas em 1993 o Lucid Emacs 19.8 importou o #'...atalho do leitor para o (function ...)MacLisp. Emacs seguiu o exemplo no ano seguinte.

phils
fonte
0

Só quero dar um exemplo prático de uso da expressão lambda backtic. Trata-se de sombreamento de variável / ligação lexical, o uso de uma expressão lambda de backtic e a referência a variáveis ​​com vírgula possibilitam obter seu valor global.

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

M-x [RET] eval-buffer saídas "idade aleatória"

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

M-x [RET] eval-buffer saídas "aleatórias"

Um terceiro exemplo que combina variável global e variável local de variável

;; -*- 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 saídas "Aleatório" "Aleatório antigo"

cjohansson
fonte
@npostavs que não era o ponto do meu exemplo, mas eu modifiquei o meu exemplo para evitar essa prática ruim também
cjohansson 18/04
Melhor, embora eu ainda não esteja convencido de que isso é uma melhoria em relação à escolha de um nome diferente para a ligação interna.
npostavs 18/04