5.1 Формы

Стандартной единицей взаимодействия с реализацией Common Lisp’а является форма, которая является объектом данных, который выполняется как программа для вычисления одного или более значений (которые также являются объектами данных). Запросить выполнение можно для любого объекта данных, но не для всех это имеет смысл. Например, символы и списки имеет смысл выполнять, тогда как массивы обычно нет. Примеры содержательных форм: 3, значение которой 3, и (+ 3 4), значение которой 7. Для обозначения этих фактов мы пишем 3  3 и (+ 3 4)  7. ( означает «вычисляется в»)

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

Все стандартные объекты данных Common Lisp, не являющиеся символами и списками (включая defstruct структуры, определённые без опции :type) являются самовычисляемыми.

5.1.1 Самовычисляемые формы

Все числа, буквы, строки и битовые векторы являются самовычисляемыми формами. Когда данный объект вычисляется, тогда объект (или возможно копия в случае с числами и строковыми символами) возвращается в качестве значения данной формы. Пустой список (), который также является значением ложь (nil), также является самовычисляемой формой: значение nil является nil. Ключевые символы (примечание переводчика: не путать с ключевыми словами в других языках, в Common Lisp’е это вид символов) также вычисляются сами в себя: значение :start является :start.

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

5.1.2 Переменные

В Common Lisp программах символы используются в качестве имён переменных. Когда символ вычисляется как форма, то в качестве результата возвращается значение переменной, которую данный символ именовал. Например, после выполнения (setq items 3), которая присвоила значение 3 переменой именованной символом items, форма items выполнится в 3 (items  3). Переменные могут быть назначены с помощью setq или связаны с помощью let. Любая программная конструкция, которая связывает переменную, сохраняет старое значение переменной, и назначает новое, и при выходе из конструкции восстанавливается старое значение.

В Common Lisp’е есть два вида переменных. Они называются лексические (или статические) и специальные (или динамические). В одно время каждая из них или обе переменные с одинаковым именем могут иметь некоторое значение. На какую переменную ссылается символ при его вычислении, зависит от контекста выполнения. Главное правило заключается в том, что если символ вычисляется в тексте конструкции, которая создала связывание для переменной с одинаковым именем, то символ ссылается на переменную, обозначенную в этом связывании, если же в тексте такой конструкции нет, то символ ссылается на специальную переменную.

Различие между двумя видами переменных заключается в области видимости и продолжительности видимости. Лексически связанная переменная может быть использована только в тексте формы, которая установила связывание. Динамически связанная (специальная) переменная может быть использована в любое время между установкой связи и до выполнения конструкции, которая упраздняет связывание. Таким образом лексическое связывание переменных накладывает ограничение на использование переменной только в некоторой текстовой области (но не на временные ограничения, так связывание продолжает существовать, пока возможно существование ссылки на переменную). И наоборот, динамическое связывание переменных накладывает ограничение на временные рамки использования переменной (но не на текстовую область). Для более подробной информации смотрите главу 3.

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

Специальная переменная может вообще не иметь значения, в таком случае, говориться, что она несвязанная. По умолчанию, каждая глобальная переменная является несвязанной, пока значение не будет назначено явно, за исключением переменных определённых в этой книге или реализацией, которые уже имеют значения сразу после первого запуска Lisp машины. Кроме того, существует возможность установки связывания специальной переменной и затем упразднения этого связывания с помощью функции makunbound. В такой ситуации переменная также называется «несвязанной», хотя это и неправильно, если быть точнее, переменная связана, но без значения FIXME. Ссылка на несвязанную переменную является ошибкой.

Чтение несвязанной переменной или неопределённой функции может быть обработано при самом высоком уровне безопасности (смотрите свойство safety декларации optimize). При других уровнях безопасности поведение не определено. Таким образом, чтение несвязанной переменной или неопределённой функции должно сигнализировать ошибку. («Чтение функции» включает ссылку на функцию используя специальный оператор function, как для f в форме (function f) и ссылку при вызове функции, как для f в форме (f x y).)

В случае inline функций (в реализациях где они поддерживаются), выполнение встраивания функци представляет собой чтение функции, таким образом нет необходимости в проверке fboundp во время исполнения. Иными словами, результат применения fmakunbound к имени inline функции не определён.

При несвязанной переменой сигнализируется ошибка unbound-variable, и слот name условия unbound-variable содержит значение имени переменной, вызвавшей ошибку.

При неопределённой функции сигнализируется ошибка unbound-function, и слот name условия unbound-function содержит значение имени переменной, вызвавшей ошибку.

Тип условия unbound-slot, которое унаследовано от cell-error, имеет дополнительный слот instance, который может быть инициализировать параметром :instance в функции make-condition. Функция unbound-slot-instance предоставляет доступ к этому слоту.

Тип ошибки, по-умолчанию сигнализирующейся для метода обобщённой функции slot-unbound CLOS, является unbound-slot. Слот instance условия unbound-slot устанавливается в соответствующий экземпляр объекта и слот name в соответствующее имя переменной.

Некоторые глобальные переменные зарезервированы в качестве «именованных констант». Они имеют глобальное значение и не могут быть связаны или переназначены. Например символы t и nil зарезервированы. Этим символам невозможно назначить значение. Также и невозможно связать эти символы с другими значениями. Символы констант определённых с помощью defconstant также становятся зарезервированными и не могут быть переназначены или связаны (но они могут быть переопределены с помощью вызова defconstant). Ключевые символы также не могут быть переназначены или связаны, ключевые символы всегда вычисляются сами в себя.

5.1.3 Специальные операторы

Если список выполняется в качестве формы, первым шагом является определение первого элемента списка. Если первый элемент списка является одним из символов, перечисленных в таблице 5.1, тогда список называется специальным оператором. (Использование слова «специальный» никак не связано с использованием этого слова в фразе «специальная переменная».)

Специальные операторы обычно являются окружениями и управляющими конструкциями. Каждый специальный оператор имеет свой идиосинкразический синтаксис. Например, специальный оператор if: (if p (+ x 4) 5) в Common Lisp’е означает то же, что и «if p then x+4 else 5» означает в Algol’е.

Выполнение специального оператора обычно возвращает одно или несколько значений, но выполнение может и вызвать нелокальный выход; смотрите return-from, go и throw.

Множество специальных операторов в Common Lisp’е фиксировано. Создание пользовательских операторов невозможно. Однако пользователь может создавать новые синтаксические конструкции с помощью определения макросов.

Множество специальных операторов в Common Lisp’е специально держится малым, потому что любая программа, анализирующая программы, должна содержать специальные знания о каждом типе специального оператора. Такие программы не нуждаются в специальных знаниях о макросах, так как раскрытие макроса просто, и далее остаётся только оперирование с результатом раскрытия. (Это не значит, что программы, в частности, компиляторы, не будут иметь специальных знаний о макросах. Компилятор может генерировать боле эффективный код, если он распознает такие конструкции, как typecase и multiple-value-bind и будет по-особому с ними обращаться.)


Таблица 5.1: Имена всех операторов

Реализация может исполнять в виде макроса любую конструкцию, описанную здесь как специальную оператор. И наоборот, реализация может выполнять в виде специального оператора любую конструкцию, описанную здесь как макрос, при условии, что также предоставляется эквивалентное определение макроса. Практическое значение заключается в том, что предикаты macro-function и special-operator-p могут оба возвращать истину, принимая один и тот же символ. Рекомендуется, чтобы программа для анализа других программ обрабатывала форму являющуюся списком с символом в первой позиции следующим образом:

  1. Если программа имеет подробные знания о символе, обрабатывать форму необходимо с помощью специализированного кода. Все символы, перечисленные в таблице 5.1 должны попадать под данную категорию.
  2. В противном случае, если для этого символа macro-function вычисляется в истину, необходимо применить macroexpand или macroexpand-1 для раскрытия формы, и результат вновь анализировать.
  3. В противном случае, необходимо расценивать форму как вызов функции.

5.1.4 Макросы

Если форма является списком и первый элемент не обозначает специальную форму, возможно он является именем макроса. Если так, то форма называется макровызовом или вызовом макроса (macrocall). Макрос это функция, которая принимает формы и возвращает формы. Возвращённые формы подставляются в то место, где происходил макровызов, и затем выполняются. (Этот процесс иногда называется раскрытием макроса.) Например, макрос с именем return принимает форму, вот так: (return x), и полученная в результате раскрытия форма такая: (return-from nil x). Мы говорим: старая форма раскрылась в новую. Новая форма будет вычислена на месте оригинальной формы. Значение новой формы будет возвращено, как значение оригинальной формы.

Макровызовы, и подформы макровызовов не обязательно должны быть Ъ списками, но использование списков с точкой требует совпадения с определением макроса, например «var» или «&rest var». Таким образом определение макроса с точкой, позволяет корректно обрабатывать макровызовы и их подформы с точкой.

В Common Lisp’е существует некоторое количество стандартных макросов, и пользователь может определять свои макросы используя defmacro.

Макросы, предоставляемые реализацией Common Lisp’а и описанные здесь, могут раскрываться в код, который не будет являться переносимым между реализациями. Вызов макроса является портабельным, в то время как результат раскрытия нет. ________________________________________________

Заметка для реализации: Implementors are encouraged to implement the macros defined in this book, as far as is possible, in such a way that the expansion will not contain any implementation-dependent special operators, nor contain as forms data objects that are not considered to be forms in Common Lisp. The purpose of this restriction is to ensure that the expansion can be processed by a program-analyzing program in an implementation-independent manner. There is no problem with a macro expansion containing calls to implementation-dependent functions. This restriction is not a requirement of Common Lisp; it is recognized that certain complex macros may be able to expand into significantly more efficient code in certain implementations by using implementation-dependent special operators in the macro expansion.

___________________________________________________________________________________________________________

5.1.5 Вызовы функций

Если список выполняется как форма, и первый элемент не является символом, обозначающим оператор или макрос, тогда предполагается, что список является вызовом функции. Первый элемент списка является именем функции. Все следующие элементы списка будут вычислены. Одно значение каждого вычисленного элемента будет является аргументом для вызываемой функции. Затем функция будет применена к аргументам. Вычисление функции обычно возвращает значение, однако вместо этого может быть выполнен нелокальный выход, смотрите throw. Функция может возвращать 0 и более значений, смотрите values. Если и когда функция возвращает значения, они становятся значениями вычисления формы вызова функции.

Например, рассмотрим вычисление формы: (+ (* 4 5)). Символ + обозначает функцию сложения, а не оператор или макрос. Таким образом две формы 3 и (* 4 5) вычисляются для аргументов. Форма 3 вычисляется в 3, а форма (* 4 5) является вызовом функции (умножения). Таким образом формы 4 и 5 вычисляются сами в себя, тем самым предоставляя аргументы для функции умножения. Функция умножения вычисляет результат 20 и возвращает его. Значения 3 и 20 становятся аргументами функции сложения, которая вычисляет и возвращает результат 23. Таким образом мы говорим (+3 (* 4 5))  23.

Тогда как аргументы в вызове функции всегда выполняются слева направо, поиск самой функции для вызова может производится как до выполнения аргументов, так и после. Программы, которые полагаются на тот факт, что поиск функции осуществляется перед выполнением аргументов, являются ошибочными.