Метапрограммирование - самостоятельный пояснительный код - учебники, статьи, книги

Я изучаю улучшающие свои навыки программирования (на самом деле я стараюсь изо всех сил стараться меньше сосать каждый год, как сказал наш Джефф Этвуд), поэтому я подумывал прочесть материал о метапрограммировании и самоочевидном коде.

Я ищу что-то вроде идиотского руководства для этого (бесплатные книги для скачивания, онлайн-ресурсы). Кроме того, я хочу больше, чем ваша средняя страница вики, а также что-то языковое агностическое или желательно с примерами Java.

Знаете ли вы о таких ресурсах, которые позволят эффективно использовать все это на практике (я знаю, что опыт может многое сказать во всем этом, но я вроде как хочу создать опыт, избегая потока плохих решений - опыт - хороший решения)?

EDIT:

Что-то вроде этого примера из Pragmatic Programmer:

... реализовать мини-язык для управления простым чертежным пакетом... Язык состоит из однобуквенных команд. За некоторыми командами следует одно число. Например, следующий ввод будет рисовать прямоугольник:

P 2 # select pen 2
D # pen down
W 2 # draw west 2cm
N 1 # then north 1
E 2 # then east 2
S 1 # then back south
U # pen up

Спасибо!

Ответ 1

Добро пожаловать в чудесный мир метапрограмм:) Мета-программирование на самом деле связано со многими вещами. Я попытаюсь перечислить то, что приходит мне на ум:

  • Макрос. Способность расширять синтаксис и семантику языка программирования сначала изучалась под терминологическим макросом. На нескольких языках есть конструкции, которые напоминают макрос, но кусок выбора, конечно, Lisp. Если вы заинтересованы в метапрограммировании, понимание Lisp, а макросистема (и гомоциклическая природа языка, где код и данные имеют одно и то же представление), окончательно обязательна. Если вам нужен диалект Lisp, который работает на JVM, перейдите к Clojure. Несколько ресурсов:

    В противном случае имеется много ресурсов о Lisp.

  • DSL. Возможность расширения синтаксиса и семантики одного языка теперь переименована под термином "DSL". Самый простой способ создать DSL - это шаблон интерпретатора. Затем внутренний DSL с свободный интерфейс и внешний DSL (согласно терминологии Фаулера). Вот хорошее видео, которое я смотрел недавно:

    Другие ответы уже указывали на ресурсы в этой области.

  • Отражение. Мета-программирование также является неотъемлемым отражением формы. Способность отражать структуру программы во время работы очень мощна. Важно тогда понять, что introspection, intercession и reification. ИМХО, отражение допускает две широкие категории вещей: 1. манипулирование данными, структура которых неизвестна во время компиляции (структура данных затем предоставляется во время выполнения, а программные кадры работают отчетливо). 2. мощные шаблоны программирования, такие как динамический прокси, заводы и т.д. Smalltalk - это выбор для исследования рефлексии, где все рефлексивно. Но я думаю, что Ruby также является хорошим кандидатом на это, с сообществом, использующим метапрограммирование (но я мало знаю о Ruby).

    Существует также богатая литература по размышлению.

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

  • Байт-код или преобразование АСТ. Это можно сделать во время компиляции или во время выполнения. Это как-то низкоуровневый подход, но также может рассматриваться как форма метапрограммирования (в некотором смысле это то же самое, что и макрос для негомиконического языка.)

Заключение: Мета-программирование - это способность программы рассуждать о себе или модифицировать себя. Точно так же, как переполнение мета-стека - это место, где можно задать вопрос о переполнении стека. Мета-программирование - это не одна конкретная техника, а скорее ансамбль концепций и методов.

Несколько вещей подпадают под сферу метапрограмм. Из вашего вопроса вы, похоже, больше интересуетесь частью макросов /DSL. Но все в конечном счете связано, поэтому другие аспекты метапрограммирования также заслуживают особого внимания.

PS: Я знаю, что большинство ссылок, которые я предоставил, не являются учебниками или вводными статьями. Это ресурсы, которые мне нравятся, которые описывают концепцию и преимущества метапрограммирования, которые, я думаю, более интересны.

Ответ 2

В вашем примере, кажется, вы говорите о доменных языках (DSL), в частности, внутренних DSL.

Здесь - большой список книг о DSL в целом (о DSL, таких как SQL).

У Мартина Фаулера есть книга, которая находится в процессе разработки и в настоящее время онлайн.

Айенде написал книгу о DSL в boo.

Обновить: (следующие комментарии)

Metaprogramming - это создание программ, которые управляют другими программами (или их данными), иногда используя DSL. В этом отношении командные файлы и сценарии оболочки можно считать метапрограммированием, поскольку они вызывают и контролируют другие программы.

В примере, который вы видите, DSL, который может использоваться метапрограммой для управления программой рисования.

Ответ 3

Я упомянул метапрограммирование шаблона С++ в своем комментарии выше. Позвольте мне поэтому привести краткий пример, используя метапрограммирование шаблонов С++. Я знаю, что вы отметили свой вопрос с помощью java, но это может быть проницательным. Надеюсь, вы сможете понять код С++.


Демонстрация на примере:

Рассмотрим следующую рекурсивную функцию, которая генерирует Серия Фибоначчи (0, 1, 1, 2, 3, 5, 8, 13,...):

unsigned int fib(unsigned int n)
{
    return n >= 2 ? fib(n-2) + fib(n-1) : n;
}

Чтобы получить элемент из серии Фибоначчи, вы вызываете эту функцию - например. fib(5) -, и он вычислит значение и вернет его вам. Пока ничего особенного.


Но теперь, на С++, вы можете переписать этот код с помощью шаблонов (несколько похожих на generics в Java), чтобы ряд Fibonacci не генерировался во время выполнения, но во время компиляции:

// fib(n) := fib(n-2) + fib(n-1)
template <unsigned int n>
struct fib                     // <-- this is the generic version fib<n>
{
    static const unsigned int value = fib<n-2>::value + fib<n-1>::value;
};

// fib(0) := 0
template <>
struct fib<0>                  // <-- this overrides the generic fib<n> for n = 0
{
    static const unsigned int value = 0;
};

// fib(1) := 1
template <>
struct fib<1>                  // <-- this overrides the generic fib<n> for n = 1
{
    static const unsigned int value = 1;
};

Чтобы получить элемент из серии Фибоначчи с помощью этого шаблона, просто получите постоянное значение - например. fib<5>::value.


Заключение ( "Что это связано с метапрограммированием?" ):

В примере шаблона это компилятор С++, который генерирует ряды Fibonacci во время компиляции, а не вашу программу во время ее запуска. (Это очевидно из того факта, что в первом примере, вы вызываете функцию, в то время как в примере шаблона вы получаете постоянное значение.) Вы получаете числа Фибоначчи, не записывая функцию, которая их вычисляет! Вместо того, чтобы программировать эту функцию, вы запрограммировали компилятор сделать что-то для вас, что оно явно не предназначено для... что весьма примечательно.

Таким образом, это одна из форм метапрограммирования:

Метапрограммирование - это написание компьютерных программ, которые пишут или управляют другими программами (или самими) в качестве их данных, или выполняют часть работы во время компиляции, в противном случае это было бы сделано во время выполнения.

- Определение из статьи в Википедии о метапрограммировании, подчеркнуто мной.

(Обратите внимание также на побочные эффекты в приведенном выше примере шаблона: поскольку вы заставляете компилятор предварительно вычислить ваши номера Фибоначчи, их нужно где-то хранить. Размер вашей двоичной программы будет увеличиваться пропорционально самому высокому n который используется в выражениях, содержащих термин fib<n>::value. Положительно, вы сохраняете время вычисления во время выполнения.)

Ответ 4

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

Если вам нужна реализация, интегрированная с Java, Jacl - это реализация Tcl в Java, которая обеспечивает возможность сценариев, ориентированных на DSL, а также доступ к любому объекту Java.

(Metaprogramming - это программы, которые пишут программы. Некоторые языки делают это намного больше, чем другие. Чтобы подобрать несколько конкретных случаев, Lisp - классический пример языка, который выполняет много метапрограммирования; С++ имеет тенденцию отбросить его к шаблонам, а не разрешать его во время выполнения, все языки сценариев, как правило, легче найти метапрограммирование, потому что их реализации написаны так, чтобы быть более гибкими, хотя это только вопрос степени.)

Ответ 5

Ну, в экосистеме Java я считаю, что самый простой способ реализовать мини-язык - использовать языки сценариев, такие как Groovy или Ruby (да, я знаю, Ruby не является гражданином java-экосистемы). Оба предлагают довольно хороший механизм спецификации DSL, который позволит вам сделать это с гораздо большей простотой, чем язык Java:

Есть, однако, чистые Java-носители, но я думаю, что их будет немного сложнее реализовать.