> 1 <
Автор | Сообщение |
Яков Замир Кацман (нью)
57 сообщений |
#7796 2017-11-06 13:25 GMT+3 часа(ов) |
;; *All what you need know about macros in lisp* отредактировал(а) Яков Замир Кацман (нью): 2017-12-11 01:03 GMT+3 часа(ов) |
|
Соотношение высоты байта к ширине не имеет значения
|
|
Яков Замир Кацман (нью)
57 сообщений |
#7812 2017-12-03 16:20 GMT+3 часа(ов) |
;; Простые макросы. отредактировал(а) Яков Замир Кацман (нью): 2017-12-03 16:29 GMT+3 часа(ов) |
|
Соотношение высоты байта к ширине не имеет значения
|
|
skelter
56 сообщений |
#7813 2017-12-05 00:49 GMT+3 часа(ов) |
> ;;Весь секрет макросов. Функция с eval?
Нет, макросы гораздо интересней. eval просто вычисляет выражение в рантайме без лексических связываний. Макрос раскрывается при компиляции, при этом порождённый код не обязан быть вычисляемым выражением. Кроме того, порождённый код живёт в лексической среде (макрос сам не знает в каких средах он может раскрываться!), и семантика порождённого кода может быть разная в разных средах. Видимо, поэтому стандарт и запрещает переопределять символы из пакета COMMON-LISP — чтобы хоть что-то было гарантировано при раскрытии макроса. |
|
Яков Замир Кацман (нью)
57 сообщений |
#7814 2017-12-05 14:16 GMT+3 часа(ов) |
Киньте пожалуйста ссылку на источник (если вас не затруднит).
Очень заинтересовала "семантика порождённого кода". Если вы позволите у меня будет еще несколько вопросов (чуть позже), это все очень интересно. Еще лучше бы здесь каких-нибудь маленьких примеров. Вроде таких: *http://lisper.ru/articles/cl-vars* Интересует, что можно сделать макросами, но нельзя никаким другим способом. Уникальные возможности макросов. отредактировал(а) Яков Замир Кацман (нью): 2017-12-05 14:37 GMT+3 часа(ов) |
|
Соотношение высоты байта к ширине не имеет значения
|
|
skelter
56 сообщений |
#7815 2017-12-07 21:01 GMT+3 часа(ов) |
Ну, насчёт eval написано в CLHS — динамические переменные из него видны, а лексические — нет. А вообще что я про макросы читал - не помню. В принципе, On Lisp и Let Over Lambda - кусками то и другое. Я, конечно, не против пообсуждать - с оговоркой, что по макросам я не эксперт и вообще, на лиспе пишу на уровне хобби.
![]() По поводу того, что макрос порождает форму, не задумываясь о его смысле — это просто определение макроса. Вот это: «порождённый код не обязан быть вычисляемым выражением» — я криво написал. Имеется в виду вот что. Вот я напишу (foo bar baz) — это осмысленное выражение или нет? Зависит. Если просто открыть свежий лисп и забить в REPL, конечно, будет ошибка: функция не определена, переменные не определены. А если загрузить библиотеку, в которой определена функция foo и глобальные переменные bar и baz, это выражение уже имеет смысл. Но даже необязательно загружать библиотеку. Это выражение может встретиться в глубине кода — и в том месте символы foo, bar и baz могут иметь какое-то значение. Например, (flet ((foo (&rest args) или даже (macrolet ((foo (bar baz) В первом случае foo стало именем функции, а bar и baz — переменными. Во втором foo превратилось в управляющую конструкцию (цикл), который три раза вызвал функцию baz. Можно определить макрос (defmacro qux () и вызовы (flet ((foo (&rest args) и (macrolet ((foo (bar baz) абсолютно эквивалентны предыдущим. Макрос qux порождает список (foo bar baz), и вычисление макроса превращается в вычисление этого списка. Все макросы порождают какие-то такие выражение, обычно более сложные. Но об окружающем коде сам макрос в принципе ничего не знает и никак не гарантирует, что получившаяся форма будет вычислена тем или иным образом, или что её в принципе можно вычислить. В таком случае говорят, что макрос захватывает символы (symbol capture). Обычно захват рассматривается как нечто нежелательное, и есть обычные способы борьбы с ним (пакеты, генсимы). Но сам по себе захват ни хорош, ни плох - просто такое свойство макросов, существующее, как говорится, независимо от нас и нашего знания о нём. Вот так - концептуально очень просто! - работают макросы в CL. Код в CL состоит из списков (из форм, точнее говоря - ведь, например, символ не список), и макрос преобразует свои аргументы в список, который рассматривается как код. Как говорят, макрос - это программа, пишущая программу. Хойт в книге Let Over Lambda делает сильный акцент на том, что лисп - программируемый язык программирования и предлагает программисту на лиспе сосредоточиться именно на этом аспекте языка. Вот, например, он пишет: Цитата То есть в обычном процедурном программировании рекомендуется писать процедуру за процедурой, чтобы каждая оставалась небольшого размера. То есть вариант написания одной монструозной процедуры, содержащей в себе всю логику и полностью решающей задачу, в обычном процедурном программировании не рекомендуется. Однако если эту монструозную процедуру сгенерировать программно? В принципе, генерирование кода - обычное дело, компиляторы им занимаются. На лиспе мы можем заниматься кодогенерацией, не выходя из лиспа. В лиспе сгенерировать монструозную процедуру - это просто составить большой, разлапистый список. И для этого у нас весь лисп в распоряжении, включая и CLOS, и те же макросы. Правда, в этом случае, даже хотя порождаемая программа имеет такое удобное и понятное представление как список, нам приходится рассматривать её как результат. Мы не привыкли. Как её моделировать? Как гарантировать, что она будет адекватно работать? Хойт пишет, что никто ещё не знает, на что способно метапрограммирование. Науке неизвестно, терра инкогнита. Можно ещё посомневаться насчёт того, как порождающую и порождаемую программы дебажить, как поддерживать. То есть в конкретном случае нужно понять, стоит ли так делать. (И житейская мудрость «если можно сделать функцией, не пиши макрос» убеждает, что не стоит.) Однако в лиспе - можно. Если метапрограммирование в целом - действительно тёмный лес, то по крайней мере у опушки мы кое-что знаем. Есть ряд стандартных задач, которые решаются макросами: например, сокращение бойлерплейта, автоматическое добавление «подчищающего» кода (например, with-open-file оборачивает тело в unwind-protect и гарантированно закрывает файл после выхода из формы), варианты управляющих конструкций, макросы для определений и т. п. В книге Грэма On Lisp рассмотрено много таких примеров. |
|
> 1 <