Common Lisp позволяет вам писать макросы, которые выполняют любые преобразования исходного кода, которые вы хотите.
Схема предоставляет вам гигиеническую систему сопоставления с образцом, которая также позволяет вам выполнять преобразования. Насколько полезны макросы на практике? Пол Грэм сказал в « Побивке средних», что:
Исходный код редактора Viaweb, вероятно, содержал около 20-25% макросов.
Какие вещи люди на самом деле делают с макросами?
Ответы:
Посмотрите на эту публикацию Матиаса Феллайзена в списке обсуждения LL1 в 2002 году. Он предлагает три основных варианта использования макросов:
источник
В основном я использую макросы для добавления новых языковых конструкций, экономящих время, которые в противном случае потребовали бы связки стандартного кода.
Например, недавно я обнаружил, что мне нужен императив,
for-loop
похожий на C ++ / Java. Однако, будучи функциональным языком, Clojure не входил в комплект поставки. Так что я просто реализовал это как макрос:И теперь я могу сделать:
И вот, у вас это есть - новая универсальная языковая конструкция во время компиляции из шести строк кода.
источник
Написание языковых расширений или DSL.
Чтобы почувствовать это на Lisp-подобных языках, изучите Racket , который имеет несколько языковых вариантов: Typed Racket, R6RS и Datalog.
Смотрите также язык Boo, который дает вам доступ к конвейеру компилятора для конкретной цели создания доменных языков с помощью макросов.
источник
Вот некоторые примеры:
Схема:
define
для определения функций. В основном это делает более короткий способ определения функции.let
для создания лексически ограниченных переменных.Clojure:
defn
согласно его документам:То же, что (имя def (fn [params *] exprs *)) или (имя def (fn ([params *] exprs *) +)) с любой строкой документа или атрибутами, добавленными в метаданные var
for
: список понятийdefmacro
: ироничный?defmethod
,defmulti
: работа с мульти-методамиns
Многие из этих макросов значительно упрощают написание кода на более абстрактном уровне. Я думаю, что макросы во многом похожи на синтаксис не-лиспов.
Библиотека черчения Incanter предоставляет макросы для некоторых сложных манипуляций с данными.
источник
Макросы полезны для встраивания некоторых шаблонов.
Например, Common Lisp не определяет
while
цикл, но имеет,do
который может быть использован для его определения.Вот пример из On Lisp .
Это выведет «12345678910», и если вы попытаетесь увидеть, что происходит с
macroexpand-1
:Это вернет:
Это простой макрос, но, как уже было сказано, они обычно используются для определения новых языков или DSL, но из этого простого примера вы уже можете попытаться представить, что вы можете с ними сделать.
loop
Макрос является хорошим примером того , что макросы могут сделать.Common Lisp имеет другой вид макросов, называемый макросом reader, который можно использовать для изменения интерпретации кода читателем, т.е. вы можете использовать их для использования # {и #} с разделителями, такими как # (и #).
источник
Вот что я использую для отладки (в Clojure):
Мне приходилось иметь дело с хеш-таблицей, созданной вручную, в C ++, где
get
метод принял в качестве аргумента неконстантную ссылку на строку, то есть я не могу вызвать ее с помощью литерала. Чтобы было легче разобраться, я написал что-то вроде следующего:Хотя что-то вроде этой проблемы вряд ли возникнет в lisp, мне особенно приятно, что у вас могут быть макросы, которые не оценивают свои аргументы дважды, например, путем введения реального let-связывания. (Допустим, здесь я мог бы обойти это).
Я также прибегаю к ужасно уродливому способу обернуть вещи в
do ... while (false)
такое, что вы можете использовать их в тогдашней части if и по-прежнему работать как-то иначе, как и ожидалось. Вам это не нужно в lisp, который является функцией макросов, работающих на синтаксических деревьях, а не на строках (или последовательностях токенов, я думаю, в случае C и C ++), которые затем подвергаются синтаксическому анализу.Существует несколько встроенных макросов потоков, которые можно использовать для реорганизации вашего кода таким образом, чтобы он читался более чётко («многопоточность», как в «совмещении кода», а не параллелизм). Например:
Он принимает первую форму
(range 6)
и делает его последним аргументом следующей формы,(filter even?)
который, в свою очередь, становится последним аргументом следующей формы и т. Д., Так что приведенное выше переписывается вЯ думаю, что первое читается гораздо яснее: «возьмите эти данные, сделайте это с ними, затем сделайте это, затем сделайте другое, и мы закончили», но это субъективно; объективно верно то, что вы читаете операции в той последовательности, в которой они выполняются (игнорируя лень).
Существует также вариант, который вставляет предыдущую форму в качестве первого (а не последнего) аргумента. Один вариант использования является арифметическим:
Читается как «возьмите 17, вычтите 2 и разделите на 3».
Говоря об арифметике, вы можете написать макрос, который выполняет синтаксический анализ инфиксных обозначений, так что вы могли бы сказать, например,
(infix (17 - 2) / 3)
и это вылилось бы(/ (- 17 2) 3)
в недостаток, заключающийся в том, что он менее читабелен и имеет преимущество в том, что он является допустимым выражением lisp. Это часть подъязыка DSL / данных.источник