Когда нужно резко цитировать лямбда-выражение?

30

Q: Когда, если вообще, полезно ли ставить острые кавычки lambda, и когда, если вообще, мы не должны резкие кавычки lambda?

Люди используют lambdas тремя способами:

  1. равнина: (lambda (x) x)
  2. цитировал: '(lambda (x) x)
  3. резкое цитируемое: #'(lambda (x) x)

Этот поток SO обсуждает три типа, этот поток SO объясняет, почему не заключать в кавычки (примечание: не резкие кавычки ) lambda, и этот поток SO также обсуждает различия между цитированием и точными кавычками.

Теперь ручной узел по анонимным функциям и строка документации для lambdaпримечания, что lambdas являются самоквитируемыми:

Звонок формы (lambda ARGS DOCSTRING INTERACTIVE BODY)- это само цитирование; Результатом вычисления лямбда-выражения является само выражение. Лямбда-выражение затем может рассматриваться как функция ...

Итак, кажется, что (lambda (x) x)и #'(lambda (x) x)эквивалентны, но '(lambda (x) x)это не так (главное, при байтовой компиляции).

Похоже , что один редко хотите процитироватьlambda , но непонятно мне , когда, если когда - либо, мы должны или не должны, с острыми цитирую:

  • Острые цитаты - это lambdaпросто стилистический выбор, или есть обстоятельства, при которых резкие цитаты действительно полезны?
  • Существуют ли обстоятельства, при которых мы не должны ставить точные кавычки lambda, то есть когда это меняет смысл кода?
Дэн
источник

Ответы:

28

Давным-давно острая цитата была необходима для лямбд, теперь это уже не так.

Итак, похоже, что (лямбда (х) х) и # '(лямбда (х) х) эквивалентны, но' (лямбда (х) х) нет (что особенно важно при байтовой компиляции).

Да. На самом деле, первые два полностью идентичны при оценке. Как описано на странице руководства, на которую вы ссылаетесь:

Следующие формы все эквивалентны:

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

За исключением попыток поддержки версий Emacs, сделанных два десятилетия назад, никогда не было причин для резкого цитирования лямбды.

Так что не надо.


В качестве обозначения:

  • Жесткое цитирование лямбды (с ') имеет значение, это предотвращает компиляцию байтов. Я не могу придумать сценарий, где это полезно, но кто знает.

  • Бэктик - единственная цитата, которая действительно полезна с лямбдами, но только если по какой-то причине вы не используете лексическое связывание.

Malabarba
источник
Попробуйте добавить ссылку в раздел « Анонимные функции » руководства, в котором содержится пример, объясняющий влияние цитирования на байтовую компиляцию.
Константин
@ Константин Готово. Мне стало лень, потому что я разговариваю по телефону, и ОП уже все равно связал его.
Малабарба
Можете ли вы уточнить, что вы имеете в виду, когда не используете лексическое связывание и обратную черту? Спасибо.
coredump
@coredump При динамическом связывании единственный способ сделать внешние переменные доступными внутри лямбды - это вручную построить лямбда в виде списка с переменной внутри. Backtics хороши для такого рода вещей.
Малабарба
Кстати, я не думаю, что «когда-то давно» действительно применимо: когда я исследовал эту тему в истории ревизий, я обнаружил, что lambdaона была определена как макрос, который добавляет значение « functionкак можно раньше». IOW, если #'в какой-то момент это было необходимо, это было в самом раннем коде разработки. Это точно не было нужно уже в Emacs-18.
Стефан
5

Так как lambdaне имеет никакого смысла , когда он не цитирует, последние версии Emacs Lisp следует (ANSI) Common Lisp в интерпретации некотируемых , (lambda...)как #'(lambda...). Эти два обозначения почти в точности эквивалентны (кроме как в цитируемой структуре).

Предпочитать (lambda...)или #'(lambda...), следовательно, чисто вопрос стиля. Некоторые люди предпочитают голую форму, которая избегает синтаксического шума, в то время как другие (включая меня) предпочитают цитируемую форму.

JCH
источник
Это противоречит руководству elisp: «В Emacs Lisp такой список является допустимым выражением, которое оценивает функциональный объект».
Джехлин
8
«Последние версии», как в «версиях, выпущенных после 1990 года или около того» ;-)
Stefan
0

Добавим немного дополнительной истории, потому что увидим # '(лямбда ...) историческое наследие?

https://debbugs.gnu.org/cgi/bugreport.cgi?bug=4290 предполагает, что:

Начиная с Emacs 22, lambdaформа байтово компилируется, когда она используется как функция, независимо от того, предшествует ей functionили #'. В версиях Emacs до 22 вы должны явно использовать #' или, functionесли вы хотите, чтобы форма была скомпилирована побайтово.

Я не знаю о байт-компиляторе, но я вижу, что, по крайней мере, еще в 1993 году сам lambdaмакрос возвратил (function (lambda ...))форму.

https://www.iro.umontreal.ca/~monnier/hopl-4-emacs-lisp.pdf также говорит:

Интересно, что (в отличие от MacLisp), lambdaтехнически не было частью языка Elisp до 1991 года, когда он был добавлен в качестве макроса, в начале разработки Emacs-19. В Emacs-18 анонимные функции записывались в виде значений в кавычках в виде:

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

Хотя lambdaмакрос делает эту цитату ненужной в течение почти 30 лет, многие примеры этой практики все еще встречаются в коде Elisp, даже если он предотвращает байтовую компиляцию тела. В некотором роде, только в 1993 году Lucid Emacs 19.8 импортировал #'...сокращение для (function ...)MacLisp. Emacs последовал их примеру год спустя.

Phils
источник
0

Просто хочу привести практический пример использования лямбда-выражения backtic. Речь идет о лексическом связывании / теневом копировании переменных, использование лямбда-выражения backtic и обращение к переменным через запятую позволяет получить их глобальное значение.

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

M-x [RET] eval-buffer выходы "Случайный старый"

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

M-x [RET] eval-buffer выходы "Random"

Третий пример объединения глобальной переменной и локальной переменной

;; -*- 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 выводит "Random", "Random old"

cjohansson
источник
@npostavs, с моим примером это было не так, но я изменил свой пример, чтобы избежать этой плохой практики
cjohansson
Лучше, хотя я все еще не уверен, что это улучшение по сравнению с простым выбором другого имени для внутренней привязки.
npostavs