Когда / почему я должен использовать progn?

19

Я видел progn, как меня довольно часто используют, просматривая файлы конфигурации опытных пользователей Emacs. Я нашел это хорошее объяснениеprogn , но что меня действительно интересует, так это какая польза от использования этой функции? Возьмем, к примеру, этот фрагмент (взят из конфигурации Саши Чуа ):

(use-package undo-tree
  :defer t
  :ensure t
  :diminish undo-tree-mode
  :config
  (progn
    (global-undo-tree-mode)
    (setq undo-tree-visualizer-timestamps t)
    (setq undo-tree-visualizer-diff t)))

Есть ли существенная разница между вышеуказанной конфигурацией и этой?

(use-package undo-tree
  :defer t
  :ensure t
  :diminish undo-tree-mode
  :config
  (global-undo-tree-mode)
  (setq undo-tree-visualizer-timestamps t)
  (setq undo-tree-visualizer-diff t))

Я чувствую, что первый пример несколько чище, хотя у него больше синтаксиса, и моя интуиция заключается в том, что при использовании может быть какое-то повышение производительности progn, но я не уверен. Спасибо за любые идеи!

elethan
источник
7
В этом конкретном случае нет никакой разницы: use-packageобтекание prognформ: config, если оно отсутствует. Попробуйте: вы можете поставить точку в конце a (use-package ...)и вызвать, M-x pp-macroexpand-last-sexpчтобы увидеть, как раскрывается макрос. Вы увидите, что это идентично для этих двух примеров.
glucas
Пример, где prognэто необходимо: emacs.stackexchange.com/questions/39172/…
npostavs

Ответы:

11

prognобычно используется при работе с макросами. Некоторые макросы ( use-packageэто макрос, который я последний раз проверял) принимают только 1 форму , где другие используют все оставшиеся формы .

progn используется в первом случае, чтобы превратить последовательность форм в одну форму.

В ваших примерах используется первый prognи, следовательно, есть 1 форма после :config. Во втором есть 3 формы . Если use-packageмакрос ожидает только 1 следующую форму :config, он вызовет ошибку.

Стоит отметить, что использование prognработает в обоих случаях, а его пропуск работает только в том случае, если макрос принимает несколько форм. В результате этого некоторые люди предпочитают просто всегда использовать progn, потому что это всегда будет работать.

Дж Дэвид Смит
источник
15
Это действительно не имеет ничего общего с макросами. Некоторые функции и макросы имеют так называемый «неявный progn», что означает, что они принимают любое количество секций в качестве отдельных аргументов и оценивают их последовательно. Другие этого не делают, и вместо этого ожидают / допускают только один аргумент, в котором вы, возможно, захотите оценить несколько сексов по порядку. Это все, что prognнужно: он позволяет вам предоставить один sexp, который при оценке вычисляет аргументы sexp по prognпорядку. Сравните (if true (progn a b c))с (when true a b c). В обоих случаях a, bи cоцениваются в последовательности.
Дрю
4
Я не согласен с тем, что 99% случаев использования предназначены для макросов и т. Д. Любой функции или макросу может быть передано prognсексп, который содержит несколько секпсов, которые оцениваются на предмет их побочных эффектов, прежде чем возвращать результат последнего из них. Это действительно не имеет ничего общего с макросами. Макросы "как пример" - это хорошо. Создавать впечатление, что prognсуществует для макросов, и что 99% его использования для макросов, вводит в заблуждение, ИМО. prognРечь идет о сведении последовательности оценок в один пол (чтобы соответствовать заданному ожидаемому аргументу), и поэтому о побочных эффектах .
Дрю
5
1. prognбыл представлен в Lisp 1.5 (см. Раздел « Возможности программы» ) в 1962 году, задолго до того, как в Lisp были добавлены макросы. Цель состоит в том, чтобы последовательно оценить выражения для их побочных эффектов. 2. Узнайте, prognкак Emacs сам знакомит prognпрограммистов с Elisp. Смотрите zap-to-charкод для простого варианта использования.
Дрю
2
Мы можем не соглашаться. Я хочу сказать, что prognэто не имеет ничего общего с макросами. Его назначение, конечно, « передать несколько форм как одну форму » (ваши слова) - как функции, так и макросу или специальной форме, но также и « Eval BODY формы последовательно и возвращаемое значение последней » (строка документа) ). Теперь как всегда («эти дни», действительно!). Упорядоченная оценка важна только для побочных эффектов, очевидно. Данный вариант использования (макрос или нет) может или не может заботиться о порядке оценки, но это не имеет значения. И Emacs Lisp не является исключительным в этом отношении.
Дрю
10

Наиболее важная причина для progn описана в первой строке документации по progn (выделение добавлено):

progn - это специальная форма, которая заставляет каждый из своих аргументов вычисляться последовательно, а затем возвращает значение последнего.

Приложение:

Без progn последовательность не гарантируется, особенно если последующие выражения зависят от побочных эффектов или возвращаемых значений предыдущих выражений. progn принудительно выполняет последовательность выполнения так же, как текстовая последовательность. Помогает не путать исполнение с разбором. Это поведение восходит к основам структур управления LISP и функционального программирования. Вот выдержка (выделение выделено) из справочного руководства по lisp :

Встроенные управляющие структуры представляют собой специальные формы, поскольку их подчиненные формы не обязательно оцениваются или не оцениваются последовательно .

Есть ли progn повысить производительность?

Разбор производительности, нет. Исполнение исполнения, нет. В лучшем случае это может быть равно, но никогда не может волшебным образом повысить производительность.

Когда используется прог ?

... чаще всего внутри защиты от раскрутки и, или , или в тогдашней части if .

Пользователь Emacs
источник
Я не уверен, почему это так важно. Emacs всегда оценивает последовательно.
lunaryorn
2
@lunaryorn смотрите обновленный ответ.
Emacs User
Я не уверен, понимаю ли я ваше редактирование. Естественно, существуют специальные формы с другим порядком оценки (например if), но обычный порядок оценки (например, формы верхнего уровня, функциональные тела и т. Д.) Является текстовым. Конечно, в некоторой степени это неявно progn, но обычно это не то, что вы используете progn в своем собственном коде.
lunaryorn
Я думаю, что ручной узел Elisp, на (elisp) Sequencingкоторый вы ссылаетесь, говорит сам за себя.
Василий
10

Лучший способ понять, что prognесть, сравнивая это с семьей: prog1и prog2. nИли 1или 2часть имени выступает за утверждение из списка, результат которого вас интересует. Другими словами, prognвозвращает результат последнего утверждения он содержит, в то время как prog1будет возвращать первый, и аналогично для prog2.

Сегодня эта функциональность кажется немного неловкой, поскольку мы учимся либо ожидать, что программа вернётся в последнем утверждении, либо явно указывать ей, что возвращать. Таким образом prog1и prog2очень редко используются. Однако, если вы думаете об этом, это имеет смысл, и вот как:

Различные языки программирования используют разные стратегии для описания своей семантики. Семейство Лисп раньше было тесно связано с денотационной семантикой. Не вдаваясь в подробности, этот вид семантики имеет особые трудности с тем, что мы стали называть «заявлениями», которые не являются «выражениями». Значения кода обычно рассматриваются в терминах комбинаций функций, в то время как с «оператором», который даже не может быть описан как функция (поскольку он ничего не оценивает), таким образом, трудно иметь дело. Однако, поскольку Lisp допускает побочные эффекты, иногда программисту хотелось бы использовать выражение, не используя его значение таким образом, чтобы оно не влияло на выражение, следующее за ним. И вот progXтут приходит семья.

В C-подобных языках эта функция иногда известна как точка последовательности (т.е. ;и, ,например). prognстоль же важен для языков, подобных Лисп, как и для языков, ;подобных С. Это фундаментальная особенность языка, которую нельзя легко заменить. Однако, в отличие от языков в стиле C, Lisp имеет тенденцию создавать синтаксические абстракции, полностью скрывая синтаксис нижнего уровня. И это, возможно, почему вы не видите, prognиспользовали все это часто, но это один из важных строительных блоков, когда речь идет о построении языковых абстракций более высокого уровня (например, макросов).

wvxvw
источник