Язык может иметь мощные макросы Lisp без круглых скобок?

Язык может иметь мощные макросы Lisp без круглых скобок?

34
задан andi 27 April 2010 в 11:50
поделиться

11 ответов

Конечно, вопрос в том, удобны ли макросы и насколько они мощны.

Давайте сначала посмотрим, чем немного отличается Лисп.

Синтаксис Лиспа основан на данных, а не на тексте

Лисп имеет двухэтапный синтаксис.

A) сначала идет синтаксис данных для s-выражений

примеры:

(mary called tim to tell him the price of the book)

(sin ( x ) + cos ( x ))

s-выражения - это атомы, списки атомов или списки.

Б) во-вторых, синтаксис языка Лисп находится поверх s-выражений. Не каждое s-выражение является допустимой программой на Лиспе.

(3 + 4)

не является допустимой программой на Лиспе, потому что Лисп использует префиксную нотацию.

(+ 3 4)

- допустимая программа на Лиспе. Первый элемент - это функция - здесь функция + .

S-выражения - это данные

Интересно то, что теперь s-выражения можно читать, а затем Lisp использует обычные структуры данных (числа, символы, списки, строки) для их представления.

Большинство других языков программирования не имеют примитивного представления для внутреннего источника - кроме строк.

Обратите внимание, что s-выражения здесь не представляют AST (абстрактное синтаксическое дерево). Это больше похоже на иерархическое дерево токенов, выходящее из фазы лексического анализатора. Лексический анализатор определяет лексические элементы.

Внутренний исходный код теперь упрощает вычисления с помощью кода, поскольку могут применяться обычные функции для управления списками.

Простые манипуляции с кодом с помощью функций списка

Давайте посмотрим на недопустимый код Lisp:

(3 + 4)

Программа

(defun convert (code)
   (list (second code) (first code) (third code)))

(convert '(3 + 4)) -> (+ 3 4)

преобразовала инфиксное выражение в допустимое префиксное выражение Lisp. Тогда мы сможем это оценить.

(eval (convert '(3 + 4))) -> 7

EVAL оценивает преобразованный исходный код. eval принимает в качестве входных данных s-выражение, здесь список (+ 3 4) .

Как выполнять вычисления с помощью кода?

В языках программирования теперь есть по крайней мере три варианта, позволяющие сделать исходные вычисления возможными:

  1. основывать преобразования исходного кода на преобразованиях строк

  2. использовать аналогичную примитивную структуру данных, такую ​​как Lisp. Более сложный вариант этого - синтаксис, основанный на XML. Затем можно было преобразовать выражения XML. Есть и другие возможные внешние форматы в сочетании с внутренними данными.

  3. используют реальный формат описания синтаксиса и представляют исходный код в виде синтаксического дерева с использованием структур данных, которые представляют синтаксические категории. -> используйте AST.

Для всех этих подходов вы найдете языки программирования. Lisp более или менее находится в лагере 2. Следствие: теоретически он не совсем удовлетворительный и делает невозможным статический синтаксический анализ исходного кода (если преобразования кода основаны на произвольных функциях Lisp). Сообщество Lisp борется с этим на протяжении десятилетий (см., Например, множество подходов, которые опробовало сообщество Scheme). К счастью, он относительно прост в использовании по сравнению с некоторыми альтернативами и довольно мощный. Вариант 1 менее элегантен. Вариант 3 приводит к большой сложности простых И сложных преобразований. Обычно это также означает, что выражение уже было проанализировано с учетом грамматики конкретного языка.

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

Lisp - это своего рода локальный максимум простоты для преобразования кода.

Другой синтаксис внешнего интерфейса

Также обратите внимание, что функция read считывает s-выражения во внутренние данные. В Лиспе можно было либо использовать другой ридер для другого внешнего синтаксиса, либо повторно использовать встроенный в Лисп читатель и перепрограммировать его с помощью механизма макроса чтения - этот механизм позволяет расширить или изменить s-выражение. синтаксис. Есть примеры для обоих подходов, обеспечивающих различный внешний синтаксис в Лиспе.

Например, есть варианты Лиспа с более традиционным синтаксисом, когда код разбирается на s-выражения.

Почему синтаксис на основе s-выражений популярен среди программистов Lisp?

Текущий синтаксис Lisp популярен среди программистов Lisp по двум причинам:

1) данные это код есть данные idea позволяет легко писать всевозможные преобразования кода на основе интернализованных данных. Существует также относительно прямой путь от чтения кода через манипулирование кодом к его печати. Можно использовать обычные инструменты разработки.

2) текстовый редактор может быть запрограммирован прямым способом для манипулирования s-выражениями. Это упрощает преобразование базового кода и данных в редакторе.

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

41
ответ дан 27 November 2019 в 16:05
поделиться

Нет, в этом нет необходимости. Все, что дает вам какой-то доступ к дереву синтаксического анализа, будет достаточно, чтобы позволить вам манипулировать телом макроса так же, как это делается в Common Lisp. Однако, поскольку манипулирование AST в lisp идентично манипулированию списками (что-то, что граничит с easy в семействе lisp), возможно, это не так естественно, если бы "дерево синтаксического анализа" и "письменная форма" были по существу такой же.

1
ответ дан 27 November 2019 в 16:05
поделиться

Да. Круглые скобки в Лиспе используются классическим способом, как механизм группировки. Отступы - альтернативный способ выразить группы. Например. следующие структуры эквивалентны:

A ((B C) D)

и

A
    B
    C
  D
5
ответ дан 27 November 2019 в 16:05
поделиться

Скобки не имеют отношения к макросам. Это просто способ работы Лиспа.

Например, в Prolog есть очень мощный механизм макросов, называемый «расширение терминов». По сути, всякий раз, когда Prolog читает термин T, if пытается использовать специальное правило term_expansion (T, R) . В случае успеха интерпретируется содержимое R вместо T.

9
ответ дан 27 November 2019 в 16:05
поделиться

Совершенно верно. Это просто на пару порядков сложнее, если вам приходится иметь дело со сложной грамматикой. Как заметил Питер Норвиг :

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

>>> parse ("2 + 2")

['eval_input', ['testlist', ['test', [ 'and_test', ['not_test', ['сравнение', ['expr', ['xor_expr', ['and_expr', ['shift_expr', ['arith_expr', ['термин', ['коэффициент', ['мощность', ['атом', [2, '2']]]]], [14, '+'] , ['термин', ['фактор', ['мощность', ['атом', [2, '2']]]]]]]]]]]] ]]], [4, ''], [0, '']]

Это было скорее разочарованием для меня. Синтаксический анализ эквивалентного выражения на Лиспе: (+ 2 2) . Кажется, что только настоящий эксперт захочет манипулировать деревьями синтаксического анализа Python, тогда как деревья синтаксического анализа Lisp просты в использовании для всех. По-прежнему можно создать что-то похожее на макросы в Python путем объединения строк, но это не интегрировано с остальной частью языка, и поэтому на практике это не делается.

Поскольку я не супергений (и даже не Питер Норвиг), я буду придерживаться (+2 2) .

20
ответ дан 27 November 2019 в 16:05
поделиться

Да, конечно, возможно. Особенно, если это все еще Lisp под капотом:

http://www.meta-alternative.net/pfront.pdf

http://www.meta-alternative.net/pfdoc.pdf

0
ответ дан 27 November 2019 в 16:05
поделиться

Не говоря уже о языке Дилана , который имеет довольно мощную синтаксическую макросистему, которая отличается (среди прочего) ссылочной прозрачностью, будучи инфиксным (в стиле Алгола) языком.

5
ответ дан 27 November 2019 в 16:05
поделиться

Вот сокращенная версия Райнера ответ:

Чтобы иметь макросы в стиле lisp, вам нужен способ представления исходного кода в структурах данных. В большинстве языков единственная «структура данных исходного кода» - это строка, которая не имеет почти достаточной структуры, чтобы позволить вам делать настоящие макросы. Некоторые языки предлагают реальную структуру данных, но она слишком сложна, как Python, так что писать настоящие макросы глупо сложно и не стоит того.

Списки и круглые скобки Lisp - это золотая середина. Достаточно структуры, чтобы с ним было легко справиться, но не слишком много, чтобы вы утонули в сложности. В качестве бонуса, когда вы вкладываете списки, вы получаете дерево, которое оказывается в точности той структурой, которую естественным образом принимают языки программирования (почти все языки программирования сначала разбираются в «абстрактное синтаксическое дерево», или AST, прежде чем будут фактически интерпретированы / скомпилированы. ).

По сути, программирование на Лиспе - это непосредственное написание AST, а не написания какого-либо другого языка, который затем превращается компьютером в AST. Вы могли бы отказаться от парен, но вам просто нужен другой способ сгруппировать вещи в список / дерево. Вы, вероятно, мало от этого выиграете.

13
ответ дан 27 November 2019 в 16:05
поделиться

Переписывание кода в Tcl способом, узнаваемым схожим с макросами Lisp, является распространенной техникой. Например, это (тривиальный) код, который упрощает написание процедур, которые всегда импортируют определенный набор глобальных переменных:

proc gproc {name arguments body} {
    set realbody "global foo bar boo;$body"
    uplevel 1 [list proc $name $arguments $realbody]
}

При этом все процедуры, объявленные с помощью gproc xyz , а не proc xyz будет иметь доступ к глобальным объектам foo , bar и boo . Весь ключ в том, что uplevel принимает команду и оценивает ее в контексте вызывающего , а list является (среди прочего) идеальным конструктором для подстановки -безопасные фрагменты кода.

3
ответ дан 27 November 2019 в 16:05
поделиться

У Boo есть красивый синтаксис макроса в кавычках, который использует [| |] в качестве разделителей и имеет определенные подстановки, которые фактически проверяются синтаксически конвейером компилятора с помощью $ переменных. Хотя его просто и относительно безболезненно использовать, его гораздо сложнее реализовать на стороне компилятора, чем s-выражения. Решение Boo может иметь несколько ограничений, которые не повлияли на мой собственный код. Существует также альтернативный синтаксис, который больше похож на обычный объектно-ориентированный код, но попадает в категорию «не для слабонервных», например, работа с деревьями синтаксического анализа Ruby или Python.

0
ответ дан 27 November 2019 в 16:05
поделиться

Преобразования разбора в Erlang по силе похожи на макросы Лиспа, хотя их гораздо сложнее писать и использовать (они применяются ко всему исходному файлу, а не вызываются по требованию).

У самого Лиспа был короткий опыт использования непарентезированного синтаксиса в виде М-выражений. Это не прижилось в сообществе, хотя варианты этой идеи нашли свой путь в современных Лиспах, так что вы получаете мощные макросы Лиспа без круглых скобок... в Лиспе!

2
ответ дан 27 November 2019 в 16:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: