1.2 Условные обозначения

Для выразительности в книге используется некоторое количество условных обозначений.

1.2.1 Десятичные числа

Все числа в данной книге представлены в десятичной системе счисления кроме мест, где система счисления указывается явно. (Конечно, десятичная система обычно и используется в работе. К несчастью, в некоторых других диалектах Lisp’а, в частности MacLisp’а, нотацией по умолчанию является восьмеричная (основание 8), вместо десятичной, и использование десятичной системы в описании Common Lisp’а в историческом контексте слегка необычно!)

1.2.2 Nil, False и пустой список

В Common Lisp’е, как и во многих других диалектах Lisp’а, символ nil используется для представления пустого списка и булева значения «ложь». Пустой список, конечно, может также быть записан как (); как правило, это просто другая запись nil. (Надо сказать, что существует возможность переопределить с помощью системы пакетов значение nil, которое впоследствии будет обозначать не пустой список, а что-то другое, однако мы не будем это здесь рассматривать, чтобы не запутывать читателя.) Эти два обозначения могут использоваться взаимозаменяемо настолько, насколько позволяет Lisp. В данной книге используется обозначение (), когда необходимо подчеркнуть использование пустого списка, и nil, когда обозначается булево значение «ложь». Запись ’nil (обратите внимание на явный знак кавычки) используется для подчеркивания того, что обозначение используется как символ. Например:

(defun three () 3) ;Обозначает пустой список параметров
(append ’() ’())  () ;Обозначает использование пустых списков
(not nil)  t ;Подчёркивает использование как булева значения «ложь»
(get ’nil ’color) ;Подчёркивает использование как символа

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

1.2.3 Вычисление, Раскрытие и Равенство

Выполнение Lisp кода называется вычисление, так как результат его выполнения обычно возвращает некоторый объект данных, называемый значением. Для обозначения вычисления в примерах используется символ  . Например,

(+ 4 5)  9

означает «результатом вычисления кода (+ 4 5) является (или будет, или был) 9».

Символ  используется в примерах для обозначения раскрытия макросов. Например,

(push x v)  (setf v (cons x v))

означает «результатом раскрытия макроса формы (push x v) является (setf v (cons x v))». Это подразумевает, что две части кода делают одно и то же действие; вторая часть кода является определением того, что делает первая.

Символ  используется в примерах для обозначения эквивалентности (тождественности). Например,

(gcd x (gcd y z))  (gcd (gcd x y) z)

означает «значение и побочные эффекты вычисления формы (gcd x (gcd y z)) всегда являются такими же, как и значение и побочные эффекты (gcd (gcd x y) z) для любых значений переменных x, y и z». Это подразумевает, что две части кода делают одинаковые вещи. Однако ни одна из них не определяет другую напрямую — так, как это делается при раскрытии макроса.

1.2.4 Ошибки

Когда в книге для некоторых возникающих ситуаций указывается, что «это ошибка», это означает:

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

Однако в некоторых ситуациях, если это обозначено в книге, «сигнализируется ошибка», и это значит:

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


Таблица 1.1: Образец описания функций
[Функция] sample-function arg1 arg2 &optional arg3 arg4
Функция sample-function складывает вместе arg1 и arg2 и полученную сумму умножает на arg3. Если arg3 не задан или равен nil, умножения не производится. sample-function затем возвращает список, в котором первый элемент содержит результат, а второй элемент равен arg4 (который по умолчанию равен символу foo). Например:
(sample-function 3 4)  (7 foo)
(sample-function 1 2 2 ’bar)  (6 bar)

В целом, (sample-function x y)  (list (+ x y) ’foo).


Таблица 1.2: Образец описания переменной
[Переменная] *sample-variable*
Переменная *sample-variable* задаёт, сколько раз специальная форма sample-special-form должна выполняться. Значение должно быть всегда неотрицательным числом или nil (что значит, выполнение бесконечно много раз). Начальное значение 0 (означает отсутствие выполнения).
Таблица 1.3: Образец описания константы
[Константа] sample-constant
Именованная константа sample-constant имеет своим значением число с плавающей точкой, равное высоте экрана в ярдах, умноженной на логарифм по основанию 2 от общего объёма в байтах, занимемого реализацией Common Lisp’а на диске.


Таблица 1.4: Образец описания оператора
[Специальный оператор] sample-special-form [name] ({var}*) {form}+
Производит вычисление каждой формы в последовательности, как неявный progn, и делает это столько раз, сколько обозначено в глобальной переменной *sample-variable*. Каждая переменная var связывается и инициализируется значением 43 перед первой итерацией, и освобождается после последней итерации. Имя name, если задано, может быть использовано в return-from форме для преждевременного выхода из цикла. Если цикл завершился нормально, sample-special-form возвращает nil. Например:
(setq *sample-variable* 3)
(sample-special-form () form1 form2)

Здесь вычисляется form1, form2, form1, form2, form1, form2 в указанном порядке.


Таблица 1.5: Образец описания макроса
[Макрос] sample-macro var [[declaration* | doc-string]] {tag | statement}*
Вычисляет выражения как тело prog с переменной var связанной со значением 43.
(sample-macro x (return (+ x x)))  86
(sample-macro var . body)  (prog ((var 43)) . body)



1.2.5 Описания функций и других объектов

Функции, переменные, именованные константы, операторы и макросы описываются с помощью особого типографского формата. Таблица 1.1 показывает способ, которым документируются Common Lisp функции. Первая строка определяет имя функции, способ передачи аргументов, и тот факт, что это функция. Если функция принимает много аргументов, тогда имена аргументов могут быть перенесены на две или три строки. Параграф, следующий за этим стандартным заголовком, разъясняет определение и использование данной функции, а зачастую также предоставляет примеры или связанные функции.

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

Текущий код (включая текущие имена функций) представляется в данном шрифте: (cons a b). Имена, встречающиеся в частях кода (метапеременные) пишутся наклонным шрифтом. В описании функции имена параметров предоставляются в наклонном шрифте. Слово &optional в списке параметров указывает на то, что все последующие аргументы являются необязательными. Значения по умолчанию для параметров описываются в последующем тексте. Список параметров может также включать &rest, указывающий на возможность бесконечного количества аргументов, или &key, указывающий на то, что допустимы именованные аргументы. (Синтаксис &optional/&rest/&key и в самом деле используется для этих целей при определении функций Common Lisp).

Таблица 1.2 показывает способ, с помощью которого документируются глобальные переменные. Первая строка определяет имя переменной и тот факт, что это переменная. Исключительно для удобства было принято соглашение, что все глобальные переменные Common Lisp’а имеют имена, начинающиеся и заканчивающиеся звёздочкой (asterisk).

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

Таблицы 1.4 и 1.5 показывают документирование операторов и макросов, предназначения которых тесно связаны. Они очень сильно отличаются от функций. Функции вызываются в соответствии с одним определённым неизменным механизмом. Синтаксис &optional/&rest/&key задаёт то, как функция внутренне использует свои аргументы, но не влияет на механизм вызова. В отличие от этого, каждый оператор или макрос может иметь свой особенный, индивидуальный механизм. Синтаксис Common Lisp’а задаётся и расширяется с помощью операторов и макросов.

В описании операторов или макросов слова записанные наклонным шрифтом обозначают соответствующую часть формы, которая вызывает оператор или макрос. Круглые скобки означают сами себя, и должны быть указаны как есть при вызове оператора или макроса. Квадратные скобки, фигурные скобки, звездочки, знаки плюса и вертикальные скобки является метасинтаксическими знаками. Квадратные скобки, [ и ], показывают, что заключённое в них выражение является необязательным (может встречаться ноль или один раз в данном месте); квадратные скобки не должны записываться в коде. Фигурные скобки, { и }, просто отображают заключённое в них выражение, однако после закрывающей скобки может следовать звёздочка, * или знак плюс +. Звёздочка показывает, что выражение в скобках может встречаться НОЛЬ и более раз, тогда как плюс показывает, что выражение может встречаться ОДИН и более раз. Внутри скобок, может использоваться вертикальная черта |, она разделяет взаимоисключаемые элементы выбора. В целом, запись {x}* значит, что x может встречаться ноль и более раз, запись {x}+ значит, что x может встречаться один и более раз, и запись [x] значит, что x может встречаться ноль или один раз. Такие записи также используются для описания выражений в стиле БНФ, как в таблице 22.3.

Двойные скобки, [[ и ]], показывают, что может использоваться любое количество альтернатив, перечисленных в скобках, и в любом порядке, но каждая альтернатива может использоваться не более одного раза, если только за ней нет звёздочки. Например,

p [[x | {y}* | z]] q

означает, что максимум один x, любое количество y, и максимум один z могут в любом порядке использоваться между обязательными p и q.

Стрелочка вниз, , показывает, что данная форма будет раскрываться ниже. Это делает запись [[ ]] более читаемой. Если X является некоторым нетерминальным символом, стоящим слева в некоторой БНФ форме, правая ее часть должна быть подставлена вместо символа X во всех случаях его использования. Таким образом, два фрагмента

p [[xyz-mixture]] q
xyz-mixture ::= x | {y}* | z

вместе составляют эквивалент для предыдущего примера.

В последнем примере в таблице 1.5, рассматривается использование записи с точкой. Точка, встречающаяся в выражении (sample-macro var . body), означает то, что имя body является списком форм, а не одиночной формой в конце списка. Эта запись часто используется в примерах.

В заглавной строке в таблице 1.5, запись [[ ]] означает, что может указываться любое количество определений (declaration), но максимум одна строка документации doc-string (которая может указываться перед, после, или между определениями).

1.2.6 Лисповый считыватель

Термин «Лисповый считыватель (читатель лиспового кода)» относится не к читателю этой книги, и не к какому-либо человеку, читающему код на Lisp’е, а к Lisp процедуре, которая называется read. Она читает символы из входного потока и интерпретирует их с помощью парсинга как представления Lisp объектов.

1.2.7 Обзор синтаксиса

В Common Lisp’е некоторые буквы используется в определённых целях. Полное описание синтаксиса можно прочесть в главе 22, но небольшой обзор здесь может быть также полезен:

Квадратные и фигурные скобки, вопросительный и восклицательные знаки, ([, ], {, }, ? и !) не используются ни для каких целей в стандартном синтаксисе Common Lisp. Эти символы явно зарезервированы для пользователей, преимущественно для использования в качестве макро-символов для пользовательских расширений синтаксиса (см. раздел 22.1.3).

Весь код в данной книге записан в нижнем регистре. Common Lisp не чуствителен к регистру текста программы. Внутри при обработке Common Lisp транслирует все имена символов в верхний регистр. С помощью *print-case* можно настроить регистр вывода имен символов. В данной книге, там где отображается взаимодействие пользователя и Lisp’овой системы, ввод представлен в нижнем регистре, а вывод в верхнем.

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