Могут ли функции выполнять большинство макросов в Common Lisp? [Дубликат]

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

Для меня причина в том, что я переименовал файл, и старый файл все еще был открыт.

24
задан Joshua Fox 12 February 2011 в 22:17
поделиться

7 ответов

Прежде всего Lisp имеет функции первого класса, поэтому вы также можете спросить: «Зачем мне нужны макросы в Lisp, если у меня уже есть первоклассные функции». Ответ на этот вопрос заключается в том, что первоклассные функции не позволяют вам играть с синтаксисом.

На косметическом уровне функции первого класса позволяют писать f(filename, some_function) или f(filename, lambda fh: fh.whatever(x)), но не f(filename, fh, fh.whatever(x)). Хотя, возможно, это хорошо, потому что в этом последнем случае гораздо менее ясно, откуда fh приходит неожиданно.

Более важные функции могут содержать только действительный код. Таким образом, вы не можете написать функцию более высокого порядка reverse_function, которая принимает функцию в качестве аргумента и выполняет ее «в обратном порядке», чтобы reverse_function(lambda: "hello world" print) выполнил print "hello world". С помощью макроса вы можете это сделать. Конечно, этот конкретный пример довольно глупый, но эта способность чрезвычайно полезна при встраивании доменных языков.

Например, вы не могли реализовать общую конструкцию lisp loop в python. Черт, вы даже не могли реализовать конструкцию python for ... in в python, если она не была встроена - по крайней мере, не с этим синтаксисом. Конечно, вы могли бы реализовать что-то вроде for(collection, function), но это намного меньше.

25
ответ дан sepp2k 27 August 2018 в 03:43
поделиться

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

В Python вы не можете взять какой-то код, передать его функции, которая генерирует новый код, чтобы выполнить новый код , Чтобы получить что-то вроде макросов в Python, вам нужно сгенерировать AST из вашего кода Python, изменить AST и оценить измененный AST.

Это означает, что вы не можете писать if в Python. Вы можете использовать только существующую, но вы не можете ее изменить или написать свои собственные заявления. Но макросы Lisp позволяют вам писать свои собственные заявления. Например, вы можете написать оператор fi, который ведет себя как if, но в качестве первого аргумента принимает часть else , а часть , затем - вторая.

В следующей статье описывается разница между макросами и процедурами более подробно: ftp://ftp.cs.utexas.edu/pub/garbage/cs345/schintro-v13/schintro_130.html

2
ответ дан ceving 27 August 2018 в 03:43
поделиться

В примере, отличном от lisp, например, эликсир, оператор потока управления if фактически является макросом. Функция if реализована как функция. Но для того, чтобы иметь более запоминающийся синтаксис, он также был реализован как макрос.

if true do 1+2 end
if true, do: ( 1+2 )
if(true, do: 1+2)
if(true, [do: (1+2)])
if(true, [{:do, 1+2}])

Все вышеперечисленное эквивалентно. Но первая строка - это реализация макросов if, которая, предположительно, будет расширена до функции if ниже.

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

is_number(if true do 1+2 end)
is_number(if true, do: (1+2))

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

0
ответ дан CMCDragonkai 27 August 2018 в 03:43
поделиться

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

И не стоит забывать, что макросы позволяют много более гибкий синтаксис (буквально, любой синтаксис ) для ваших eDSL и языковых расширений.

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

8
ответ дан Community 27 August 2018 в 03:43
поделиться

Вместо ответа на высоком уровне, вот конкретное предложение: прочитайте «Shine Thrram» The Swine Before Perl . Я показываю, как разработать макрос, который выполняет несколько разных действий - конкретный поток управления, привязку и язык данных. (Кроме того, вы увидите, как на самом деле делать такие вещи.)

4
ответ дан Eli Barzilay 27 August 2018 в 03:43
поделиться

Вот ответ Маттиаса Феллисена с 2002 года (через http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg01539.html ):

< blockquote>

Я хотел бы предложить, чтобы было три дисциплинированных использования макросов:

  1. data sublanguages: я могу писать простые выражения и создавать сложные вложенные списки / массивы / таблицы с цитатой , unquote и т. д., аккуратно одетый с макросами.
  2. связывания конструкций: я могу представить новые конструкции связывания с макросами. Это помогает мне избавиться от лямбды и сблизить вещи, которые принадлежат друг другу. Например, один из наших обучающих пакетов содержит форму (веб-запрос ([имя-фамилия (строка-добавление «Hello» first-name «ваша фамилия?»)) ... фамилия ... имя ...) с очевидным взаимодействием между программой и веб-потребителем подразумевается. [Примечание: в ML вы можете написать веб-запрос (fn last-name => ...) string_append (...), но golly - это боль и ненужный шаблон.]
  3. переопределение оценки: я могу ввести конструкции, которые задерживают / откладывают оценку выражений по мере необходимости. Подумайте о циклах, новых условностях, задержке / силе и т. д. [Примечание: в Haskell , вам это не нужно.]

Насколько я понимаю, Lispers использует макросы по другим причинам. «семантические» нерегулярности на целевом языке.

Я призываю людей решить все три вопроса, когда говорят, что язык X может делать то, что могут сделать макросы.

- Matthias

Felleisen является одним из mos т влиятельных макро исследователей в этой области. (Я не знаю, согласен ли он с этим сообщением.)

More reading: Пол Грэхем на Лиспе ( http://www.paulgraham.com/onlisp.html ; Грэм определенно не согласен с Felleisen, что это единственное полезное использование макросов) и статью Шрирама Кришнамурти «Автоматики через макросы» ( http: //www.cs .brown.edu / ~ ск / Публикации / Статьи / Опубликованные / SK-автоматные-макросы / ).

11
ответ дан Kyle 27 August 2018 в 03:43
поделиться

Макросы делают преобразования кода

Макрос преобразует исходный код. Леневой оценки нет. Представьте, что теперь вы можете писать функции, которые преобразуют произвольный код в произвольный код.

Очень простые преобразования кода

Создание простых языковых конструкций также является очень простым примером. Рассмотрим ваш пример открытия файла:

(with-open-file (stream file :direction :input)
  (do-something stream))

vs.

(call-with-stream (function do-something)
                  file
                  :direction :input)

Что дает мне макрос, это немного другой синтаксис и структура кода.

Встроенный язык: расширенные конструкции итераций

Далее рассмотрим несколько другой пример:

(loop for i from 10 below 20 collect (sqr i))

vs.

(collect-for 10 20 (function sqr))

Мы можем определить функцию COLLECT-FOR, который делает то же самое для простого цикла и имеет переменные для начала, конца и функции шага.

Но LOOP предоставляет новый язык. Макрос LOOP является компилятором для этого языка. Этот компилятор может выполнить LOOP конкретные оптимизации и также может проверить синтаксис во время компиляции для этого нового языка. Еще более мощный макрос цикла ITERATE. Эти мощные инструменты на уровне языка теперь могут быть написаны как библиотеки без какой-либо специальной поддержки компилятора.

Прогулка по дереву кода в макросе и внесение изменений

Следующий еще один простой пример:

(with-slots (age name) some-person
  (print name)
  (princ " "
  (princ age))

vs. что-то похожее:

(flet ((age (person) (slot-value person 'age))
       (name (person) (slot-value person 'name)))
   (print (name))
   (princ " ")
   (princ (age)))

Макрос WITH-SLOTS вызывает полное перемещение вложенного исходного дерева и заменяет имя переменной вызовом на (SLOT-VALUE SOME-PERSON 'name):

(progn
  (print (slot-value some-person 'name))
  (princ " "
  (princ (slot-value some-person 'age)))

В этом случае макрос может переписать отдельные части кода. Он понимает структуру языка Lisp и знает, что имя и возраст являются переменными. Он также понимает, что в некоторых ситуациях name и age могут не быть переменными и не должны быть переписаны. Это приложение так называемого кода Walker , инструмент, который может перемещаться по кодам деревьев и вносить изменения в дерево кода.

Макросы могут изменять среду компиляции

Еще один простой пример: содержимое небольшого файла:

(defmacro oneplus (x)
  (print (list 'expanding 'oneplus 'with x))
  `(1+ ,x))

(defun example (a b)
   (+ (oneplus a) (oneplus (* a b))))

В этом примере нас не интересует макрос ONEPLUS, а сам макрос DEFMACRO.

Что интересно? В Lisp вы можете иметь файл с указанным выше содержимым и использовать компилятор file для компиляции этого файла.

;;; Compiling file /private/tmp/test.lisp ...
;;; Safety = 3, Speed = 1, Space = 1, Float = 1, Interruptible = 1
;;; Compilation speed = 1, Debug = 2, Fixnum safety = 3
;;; Source level debugging is on
;;; Source file recording is  on
;;; Cross referencing is on
; (TOP-LEVEL-FORM 0)
; ONEPLUS

(EXPANDING ONEPLUS SOURCE A) 
(EXPANDING ONEPLUS SOURCE (* A B)) 
; EXAMPLE
;; Processing Cross Reference Information

Итак, мы видим, что компилятор file g2] расширяет использование макроса ONEPLUS.

Что особенного в этом? В файле есть определение макроса, и в следующем виде мы уже используем этот новый макрос ONEPLUS. Мы никогда не загружали определение макроса в Lisp. Как-то компилятор знает и регистрирует определенный макрос ONEPLUS и затем может его использовать.

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

С помощью функции мы не можем этого сделать. Компилятор создает код для вызовов функций, но не запускает их. Но макрос можно запустить во время компиляции и добавить «знание» в компилятор. Это знание тогда действует во время запуска компилятора и частично забывается позже. DEFMACRO - макрос, который выполняется во время компиляции, а затем информирует среду компиляции нового макроса.

Обратите внимание, что макрос ONEPLUS также запускается дважды, поскольку он используется дважды в файл. Побочным эффектом является то, что он что-то печатает. Но ONEPLUS может иметь и другие произвольные побочные эффекты. Например, он может проверить закрытый источник на базе правил и предупредить вас, если, например, закрытый код нарушает некоторые правила (подумайте о проверке стиля).

Это означает, что макрос здесь DEFMACRO , может изменять язык и среду при компиляции файла. В других языках компилятор может предоставить специальные директивы компилятора, которые будут распознаны во время компиляции. Существует множество примеров таких макросов, которые влияют на компилятор: DEFUN, DEFCLASS, DEFMETHOD, ...

Макросы могут сделать код пользователя короче

Например, макрос DEFSTRUCT для определения структур данных записи .

(defstruct person name age salary)

Выше defstruct макрос создает код для

  • новый тип структуры person с тремя слотами
  • слот-аксессуар для чтения и записи значений
  • предикат для проверки того, имеет ли какой-либо объект класс person
  • a make-person функция для создания структурных объектов
  • печатное представление

Кроме того, он может:

  • записать исходный код
  • записывает исходный код исходного кода (файл, буфер редактора, REPL, ...)
  • перекрестная ссылка на исходный код

Исходный код для определения структуры - это короткая строка. Расширенный код намного длиннее.

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

7
ответ дан Rainer Joswig 27 August 2018 в 03:43
поделиться
Другие вопросы по тегам:

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