Есть много языков, которые допускают какое-то метапрограммирование . В частности, я удивлен, что нет ответа, говорящего о семействе языков Lisp .
Из википедии:
Метапрограммирование - это написание компьютерных программ с возможностью обрабатывать программы как их данные.
Далее в тексте:
Вероятно, Лисп является наиболее существенным языком с возможностями метапрограммирования, как из-за его исторического приоритета, так и из-за простоты и мощи его метапрограммирования.
Лисп языки
Краткое введение в Lisp следует.
Один из способов просмотра кода - это набор инструкций: сделайте это, затем сделайте это, затем сделайте это другое ... Это список! Список вещей для программы, чтобы сделать. И, конечно, вы можете иметь списки внутри списков для представления циклов и так далее.
Если мы представим список , содержащий элементы а, Ь, с, d , как это: (ABCD) , мы получаем то , что выглядит как вызов функции Лиспа, где a
есть функция, и b
, c
, d
являются аргументы. Если факт типичный "Hello World!" Программа может быть написана так:(println "Hello World!")
Конечно b
, c
или d
могут быть списки, которые также оценивают что-то. Следующее: (println "I can add :" (+ 1 3) )
напечатало бы «Я могу добавить: 4».
Итак, программа - это серия вложенных списков, а первый элемент - это функция. Хорошей новостью является то, что мы можем манипулировать списками! Таким образом, мы можем манипулировать языками программирования.
Преимущество Лисп
Lisps - это не столько языки программирования, сколько инструментарий для создания языков программирования. Программируемый язык программирования.
В Лиспе не только намного проще создавать новые операторы, но также практически невозможно написать некоторые операторы на других языках, потому что аргументы оцениваются при передаче в функцию.
Например, на C-подобном языке, скажем, вы хотите написать if
оператор самостоятельно, что-то вроде:
my-if(condition, if-true, if-false)
my-if(false, print("I should not be printed"), print("I should be printed"))
В этом случае оба аргумента будут оценены и напечатаны в порядке, зависящем от порядка вычисления аргументов.
В Лиспе написание оператора (мы называем это макросом) и написание функции примерно одинаково и используются одинаково. Основное отличие состоит в том, что параметры макроса не оцениваются перед передачей в качестве аргументов в макрос. Это важно, чтобы иметь возможность писать некоторые операторы, как указано if
выше.
Языки реального мира
Здесь показано, как именно это немного выходит за рамки, но я призываю вас попробовать программирование на одном Лиспе, чтобы узнать больше. Например, вы можете взглянуть на:
- Схема , старый, довольно «чистый» Лисп с небольшим ядром
- Common Lisp, больший Lisp с хорошо интегрированной объектной системой и множеством реализаций (он стандартизирован ANSI)
- Ракетка печатного Лисп
- Clojure мой любимый, примеры выше были код Clojure. Современный Лисп, работающий на JVM. Также есть несколько примеров макросов Clojure для SO (но это не то место, с которого нужно начинать. Сначала я бы посмотрел на 4clojure , braveclojure или clojure koans )).
Да, и, кстати, Lisp означает LISt Processing.
Что касается ваших примеров
Я собираюсь привести примеры, используя Clojure ниже:
Если вы можете написать add
функцию в Clojure (defn add [a b] ...your-implementation-here... )
, вы можете назвать ее +
так (defn + [a b] ...your-implementation-here... )
. Это на самом деле то, что делается в реальной реализации (тело функции немного сложнее, но определение по сути такое же, как я писал выше).
Как насчет инфиксной записи? Ну, Clojure использует prefix
(или польскую) нотацию, поэтому мы могли бы создать infix-to-prefix
макрос, который превратил бы префиксный код в код Clojure. Что на самом деле удивительно легко (на самом деле это одно из макро упражнений в clojure koans)! Это также можно увидеть в дикой природе, например, см. Макрос Incanter$=
.
Вот самая простая версия из объяснения коанов:
(defmacro infix [form]
(list (second form) (first form) (nth form 2)))
;; takes a form (ie. some code) as parameter
;; and returns a list (ie. some other code)
;; where the first element is the second element from the original form
;; and the second element is the first element from the original form
;; and the third element is the third element from the original form (indexes start at 0)
;; example :
;; (infix (9 + 1))
;; will become (+ 9 1) which is valid Clojure code and will be executed to give 10 as a result
Чтобы продвинуться дальше, некоторые цитаты из Lisp :
«Часть того, что делает Лисп отличительным, состоит в том, что он предназначен для развития. Вы можете использовать Lisp для определения новых операторов Lisp. По мере того, как новые абстракции становятся популярными (например, объектно-ориентированное программирование), их легко реализовать в Лиспе. Как и ДНК, такой язык не выходит из моды ».
- Пол Грэм, ANSI Common Lisp
«Программирование на Лиспе похоже на игру с изначальными силами вселенной. Это похоже на молнию между кончиками пальцев. Ни один другой язык даже не чувствует себя близко ».
- Гленн Эрлих, Дорога в Лисп