Если вы получаете это сообщение во время сохранения или компиляции сборки, просто закройте все файлы, а затем откройте любой файл для компиляции и сохранения.
Для меня причина в том, что я переименовал файл, и старый файл все еще был открыт.
Прежде всего 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)
, но это намного меньше.
Макросы наиболее полезны на языках, которые используют ту же форму для данных и кода, потому что макрос обрабатывает код как данные и создает новый код. Макрораспределение - это генерация кода точно в момент времени, который выполняется на этапе компиляции до начала оценки. Это делает очень легким создание 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
В примере, отличном от 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, которые не могут выполнять стандартные функции.
Макросы расширяются во время компиляции. Закрытия строятся во время выполнения. С помощью макросов вы можете создавать высокоэффективные компиляторы встроенных доменных языков, а с помощью функций высокого порядка вы можете применять неэффективные интерпретаторы. Эти компиляторы eDSL могут выполнять всевозможные статические проверки, выполнять любые дорогостоящие оптимизации, которые вам нравятся, но когда у вас есть только время выполнения, вы ничего не можете сделать дорогостоящим.
И не стоит забывать, что макросы позволяют много более гибкий синтаксис (буквально, любой синтаксис ) для ваших eDSL и языковых расширений.
Подробнее см. ответы на этот вопрос: Коллекция отличных приложений и программ с использованием Макросы
Вместо ответа на высоком уровне, вот конкретное предложение: прочитайте «Shine Thrram» The Swine Before Perl . Я показываю, как разработать макрос, который выполняет несколько разных действий - конкретный поток управления, привязку и язык данных. (Кроме того, вы увидите, как на самом деле делать такие вещи.)
Вот ответ Маттиаса Феллисена с 2002 года (через http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg01539.html ):
< blockquote>Я хотел бы предложить, чтобы было три дисциплинированных использования макросов:
Насколько я понимаю, Lispers использует макросы по другим причинам. «семантические» нерегулярности на целевом языке.
Я призываю людей решить все три вопроса, когда говорят, что язык X может делать то, что могут сделать макросы.
- Matthias
blockquote>Felleisen является одним из mos т влиятельных макро исследователей в этой области. (Я не знаю, согласен ли он с этим сообщением.)
More reading: Пол Грэхем на Лиспе ( http://www.paulgraham.com/onlisp.html ; Грэм определенно не согласен с Felleisen, что это единственное полезное использование макросов) и статью Шрирама Кришнамурти «Автоматики через макросы» ( http: //www.cs .brown.edu / ~ ск / Публикации / Статьи / Опубликованные / SK-автоматные-макросы / ).
Макросы делают преобразования кода
Макрос преобразует исходный код. Леневой оценки нет. Представьте, что теперь вы можете писать функции, которые преобразуют произвольный код в произвольный код.
Очень простые преобразования кода
Создание простых языковых конструкций также является очень простым примером. Рассмотрим ваш пример открытия файла:
(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
make-person
функция для создания структурных объектов Кроме того, он может:
Исходный код для определения структуры - это короткая строка. Расширенный код намного длиннее.
Макрос DEFSTRUCT
не нуждается в доступе к метауровню языка для создания этих различных вещей. Он просто преобразует компактный фрагмент описательного кода в, как правило, более длинный, определяющий код, используя типичные языковые конструкции.