Коли / чому я повинен використовувати прогн?


19

Я бачив, що prognвін використовується досить багато під час перегляду файлів конфігурації досвідчених користувачів Emacs. Я знайшов це приємне поясненняprogn , але що мені дуже цікаво, в чому полягає користь від використання цієї функції? Візьмемо для прикладу цей фрагмент (узятий із конфігурації Sacha Chua ):

(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, але я не впевнений. Дякую за будь-яку інформацію!


7
У цьому конкретному випадку різниці немає: use-packageоберне prognнавколо ваших: config форм, якщо він відсутній. Спробуйте: ви можете поставити крапку в кінці a (use-package ...)і зателефонувати, M-x pp-macroexpand-last-sexpщоб побачити, як розширюється макрос. Ви побачите, що це ідентично для цих двох прикладів.
глюкас

Приклад, де 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% його використання стосується макросів, вводить в оману, IMO. prognйдеться про перенесення послідовності оцінок в єдиний сексп (щоб відповідати заданому аргументу), і тому йдеться про побічні ефекти .
Дрю

5
1. prognбула введена в Lisp 1.5 (див. Розділ Програма функції ), в 1962 році, задовго до того, як макроси були додані в Lisp. Мета - послідовно оцінювати вирази на предмет їх побічних ефектів. 2. Подивіться, prognяк Emacs сам знайомиться prognз програмістами Elisp. Дивіться zap-to-charкод для простого випадку використання.
Дрю

2
Ми можемо погодитися не погодитися. Суть в тому , що prognне має нічого спільного з макросами. Його мета, звичайно, " передавати декілька форм як єдину форму " (ваші слова) - будь-якій функції або макросу або спеціальній формі, але також " Еваль BODY форм послідовно і повертає значення останньої " (doc string ). Тепер, як ніколи ("справді в ці дні"!). Очевидно, що впорядкована оцінка важлива лише для побічних ефектів. Даний випадок використання (макро чи ні) може або не стосується порядку оцінювання, але це не має значення. І Emacs Lisp не є винятковою в цьому плані.
Дрю

10

Найважливіша причина прогн. Описана в першому рядку прогн. Документації (наголос додано):

progn - це спеціальна форма, яка змушує оцінювати послідовно кожен з його аргументів, а потім повертає значення останнього.

Додаток:

Без розголосу не гарантується послідовність, особливо якщо наступні вирази залежать від побічних ефектів або зворотних значень попередніх виразів. progn примушує послідовність виконання те саме, що і текстова послідовність. Допомагає не плутати виконання з розбором. Така поведінка сходить до основ структури керування Lisp та функціонального програмування. Ось уривок (наголос додано) з довідкового посібника Lisp :

Вбудовані структури управління - це спеціальні форми, оскільки їх підформи не обов'язково оцінюють або не оцінюють послідовно .

Є чи progn підвищити продуктивність?

Розбіжна ефективність, немає. Виконання виконання, немає. У кращому випадку це може дорівнювати, але ніколи не збільшувати продуктивність.

Коли progn використовується ?

... найчастіше всередині, що захищається, і, або , або в тодішній частині if .


Я не впевнений, чому це так важливо. Emacs завжди оцінює послідовно.
місячник

2
@lunaryorn дивіться оновлену відповідь.
Користувач Emacs

Я не впевнений, чи я розумію вашу редакцію. Природно, існують спеціальні форми з іншим порядком оцінювання (наприклад if), але звичайний порядок оцінювання (наприклад, форми вищого рівня, органи функцій тощо) є текстовим. Звичайно, певною мірою це неявно progn, але зазвичай це не те, що ви використовуєте progn у власному коді.
місячник

Я думаю, що вузол ручної роботи Elisp, (elisp) Sequencingякий ви посилаєте, говорить про це все.
Василь

10

Кращий спосіб зрозуміти, що prognтаке, порівнюючи це з сім'єю: prog1і prog2. nАбо 1чи 2частину імені виступає за утвердження зі списку, результат якого вас цікавить. Іншими словами, prognповертає результат останнього твердження він містить, в той час як prog1повертатиме перший, і аналогічно для prog2.

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

Різні мови програмування використовують різні стратегії для опису своєї семантики. Сім'я Лісп була тісно пов'язана з денотаційною семантикою. Не вникаючи в деталі, цей вид семантики має особливі труднощі з тим, що ми зросли, щоб зрозуміти як "твердження", які не є "виразами". Значення коду, як правило, розглядаються як комбінації функцій, тоді як "твердження", яке навіть не можна охарактеризувати як функцію (оскільки воно ні до чого не оцінює), важко розібратися. Однак, оскільки Лісп допускає побічні ефекти, іноді програміст бажає використовувати вираз, не використовуючи його значення таким чином, що це не впливає на вираження, що слідує за ним. І ось сюди progXзаходить родина.

У мовах, подібних С, ця функція іноді називається точкою послідовності (тобто , ;і ,, наприклад). prognдля мов, які нагадують Lisp, так само важливо, як ;і для С-подібних мов. Це основна особливість мови, яку неможливо легко замінити. Однак, на відміну від мов у стилі С, Лісп схильний будувати синтаксичні абстракції, повністю приховуючи синтаксис нижнього рівня. І це, можливо, чому ви не бачите, що prognвикористовується все так часто, але це один з важливих складових, коли мова йде про побудову мовних абстракцій вищого рівня (sa macros).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.