Конечно, вопрос в том, удобен ли этот макрос и насколько он силен.
Давайте сначала посмотрим, как Лисп немного отличается.
Синтаксис Lisp основан на данных, а не на тексте
Lisp имеет двухэтапный синтаксис.
A) сначала идет синтаксис данных для s-выражений
Примеры:
(mary called tim to tell him the price of the book)
(sin ( x ) + cos ( x ))
s-выражения - это атомы, списки атомов или списки.
B) во-вторых, есть синтаксис языка Lisp поверх s-выражений.
Не каждое s-выражение является допустимой программой на Лиспе.
(3 + 4)
не является допустимой программой на Лиспе, потому что Лисп использует префиксную нотацию.
(+ 3 4)
является допустимой программой на Лиспе. Первый элемент - это функция - здесь функция +
.
S-выражения являются данными
Теперь интересным является то, что s-выражения могут быть прочитаны, а затем Лисп использует их обычные структуры данных (числа, символы, списки, строки).
Большинство других языков программирования не имеют примитивного представления для внутреннего источника - кроме строк.
Обратите внимание, что s-выражения здесь не представляют AST (абстрактное синтаксическое дерево). Это больше похоже на иерархическое дерево токенов, выходящее из фазы лексера. Лексер определяет лексические элементы.
Внутренний исходный код теперь облегчает вычисления с помощью кода, поскольку могут применяться обычные функции для управления списками.
Простые манипуляции с кодом с помощью функций списка
Давайте посмотрим на неверный код Lisp:
(3 + 4)
Программа
(defun convert (code)
(list (second code) (first code) (third code)))
(convert '(3 + 4)) -> (+ 3 4)
преобразовал выражение infix в действительное выражение префикса Lisp. Мы можем оценить это тогда.
(eval (convert '(3 + 4))) -> 7
EVAL оценивает преобразованный исходный код. eval
принимает в качестве входных данных s-выражение, здесь список (+ 3 4)
.
Как рассчитать с кодом?
В языках программирования теперь есть как минимум три варианта, позволяющих сделать исходные вычисления:
основывать преобразования исходного кода на строковых преобразованиях
использовать такую же примитивную структуру данных, как Lisp. Более сложным вариантом этого является синтаксис на основе XML. Затем можно преобразовать выражения XML. Существуют и другие возможные внешние форматы в сочетании с интернализованными данными.
использует формат описания реального синтаксиса и представляет исходный код, интернализованный в виде синтаксического дерева, с использованием структур данных, которые представляют синтаксические категории. -> использовать AST.
Для всех этих подходов вы найдете языки программирования. Lisp более или менее находится в лагере 2. Следствие: теоретически это не совсем удовлетворительно и делает невозможным статический анализ исходного кода (если преобразования кода основаны на произвольных функциях Lisp). Сообщество Lisp борется с этим на протяжении десятилетий (см., Например, множество подходов, которые пробовало сообщество Scheme). К счастью, он сравнительно прост в использовании по сравнению с некоторыми альтернативами и довольно мощный. Вариант 1 менее элегантен. Вариант 3 приводит к большой сложности в простых И сложных преобразованиях. Обычно это также означает, что выражение уже было проанализировано относительно определенной грамматики языка.
Другая проблема - КАК преобразовать код. Один подход будет основан на правилах преобразования (как в некоторых вариантах макроса Scheme). Другим подходом может быть специальный язык преобразования (например, язык шаблонов, который может выполнять произвольные вычисления). Подход Lisp заключается в использовании самого Lisp. Это позволяет писать произвольные преобразования, используя полный язык Lisp. В Лиспе нет отдельной стадии синтаксического анализа, но в любое время выражения могут быть прочитаны, преобразованы и оценены, потому что эти функции доступны пользователю.
Lisp является своего рода локальным максимумом простоты для преобразования кода.
Другой синтаксис внешнего интерфейса
Также обратите внимание, что функция read
считывает s-выражения во внутренние данные. В Лиспе можно использовать другой reader для другого внешнего синтаксиса или повторно использовать встроенный считыватель Lisp и перепрограммировать его с помощью механизма чтения макроса - этот механизм позволяет расширять или изменять s-выражение синтаксис. Есть примеры для обоих подходов, чтобы обеспечить другой внешний синтаксис в Лиспе.
Например, есть варианты Lisp, которые имеют более обычный синтаксис, где код разбирается на s-выражения.
Почему синтаксис на основе s-выражений популярен среди программистов на Лиспе?
Текущий синтаксис Lisp популярен среди программистов на Lisp по двум причинам:
1) данные - это код - это данные Идея упрощает написание всех видов преобразований кода на основе интернализованных данных. Существует также относительно прямой путь от чтения кода, манипулирования кодом до печати кода. Можно использовать обычные средства разработки.
2) текстовый редактор может быть запрограммирован прямым способом для манипулирования s-выражениями. Это делает базовые преобразования кода и данных в редакторе относительно простыми.
Первоначально считалось, что Лисп имеет другой, более традиционный синтаксис. Позже было несколько попыток переключиться на другие варианты синтаксиса, но по некоторым причинам он либо потерпел неудачу, либо породил разные языки.