Для выразительности в книге используется некоторое количество условных обозначений.
Все числа в данной книге представлены в десятичной системе счисления кроме мест, где система счисления указывается явно. (Конечно, десятичная система обычно и используется в работе. К несчастью, в некоторых других диалектах Lisp’а, в частности MacLisp’а, нотацией по умолчанию является восьмеричная (основание 8), вместо десятичной, и использование десятичной системы в описании Common Lisp’а в историческом контексте слегка необычно!)
В Common Lisp’е, как и во многих других диалектах Lisp’а, символ nil используется для представления пустого списка и булева значения «ложь». Пустой список, конечно, может также быть записан как (); как правило, это просто другая запись nil. (Надо сказать, что существует возможность переопределить с помощью системы пакетов значение nil, которое впоследствии будет обозначать не пустой список, а что-то другое, однако мы не будем это здесь рассматривать, чтобы не запутывать читателя.) Эти два обозначения могут использоваться взаимозаменяемо настолько, насколько позволяет Lisp. В данной книге используется обозначение (), когда необходимо подчеркнуть использование пустого списка, и nil, когда обозначается булево значение «ложь». Запись ’nil (обратите внимание на явный знак кавычки) используется для подчеркивания того, что обозначение используется как символ. Например:
Любой объект данных, не являющийся nil, интерпретируется как булево значение «не ложь», которое является «истиной». Символ t обычно используется для обозначения «истины», когда нет более подходящего значения. Когда говорится, что функция «возвращает ложь» или «ложна» в некоторых случаях, это значит, что она возвращает nil. Однако если говорится, что функция «возвращает истину» или «истинна» в некоторых случаях, это значит, что она возвращает некоторое значение отличное от nil, но необязательно t.
Выполнение Lisp кода называется вычисление, так как результат его выполнения обычно возвращает некоторый объект данных, называемый значением. Для обозначения вычисления в примерах используется символ ⇒ . Например,
означает «результатом вычисления кода (+ 4 5) является (или будет, или был) 9».
Символ → используется в примерах для обозначения раскрытия макросов. Например,
означает «результатом раскрытия макроса формы (push x v) является (setf v (cons x v))». Это подразумевает, что две части кода делают одно и то же действие; вторая часть кода является определением того, что делает первая.
Символ ≡ используется в примерах для обозначения эквивалентности (тождественности). Например,
означает «значение и побочные эффекты вычисления формы (gcd x (gcd y z)) всегда являются такими же, как и значение и побочные эффекты (gcd (gcd x y) z) для любых значений переменных x, y и z». Это подразумевает, что две части кода делают одинаковые вещи. Однако ни одна из них не определяет другую напрямую — так, как это делается при раскрытии макроса.
Когда в книге для некоторых возникающих ситуаций указывается, что «это ошибка», это означает:
Имеется в виду не то, что какая-то реализация может и не задать побочные эффекты и результаты для этих ситуаций, а то, что программа, полностью соотвествующая спецификации Common Lisp’а, не должна быть зависима от подобных побочных эффектов и результатов.
Однако в некоторых ситуациях, если это обозначено в книге, «сигнализируется ошибка», и это значит:
В местах, где встречаются выражения «должен» быть или «не должен» быть, или что-то «не может» быть, подразумевается, что если указанные условия не выполняются, то «это ошибка». Например: если аргумент «должен быть символом», а аргумент не символ, тогда «это ошибка». Во всех случаях, где ошибка сигнализируется, всегда явно используется слово «сигнализируется (генерируется)».
В целом, (sample-function x y) ≡ (list (+ x y) ’foo).
Здесь вычисляется form1, form2, form1, form2, form1, form2 в указанном порядке.
Функции, переменные, именованные константы, операторы и макросы описываются с помощью особого типографского формата. Таблица 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.
Двойные скобки, [[ и ]], показывают, что может использоваться любое количество альтернатив, перечисленных в скобках, и в любом порядке, но каждая альтернатива может использоваться не более одного раза, если только за ней нет звёздочки. Например,
Стрелочка вниз, ↓, показывает, что данная форма будет раскрываться ниже. Это делает запись [[ ]] более читаемой. Если X является некоторым нетерминальным символом, стоящим слева в некоторой БНФ форме, правая ее часть должна быть подставлена вместо символа ↓X во всех случаях его использования. Таким образом, два фрагмента
В последнем примере в таблице 1.5, рассматривается использование записи с точкой. Точка, встречающаяся в выражении (sample-macro var . body), означает то, что имя body является списком форм, а не одиночной формой в конце списка. Эта запись часто используется в примерах.
В заглавной строке в таблице 1.5, запись [[ ]] означает, что может указываться любое количество определений (declaration), но максимум одна строка документации doc-string (которая может указываться перед, после, или между определениями).
Термин «Лисповый считыватель (читатель лиспового кода)» относится не к читателю этой книги, и не к какому-либо человеку, читающему код на Lisp’е, а к Lisp процедуре, которая называется read. Она читает символы из входного потока и интерпретирует их с помощью парсинга как представления Lisp объектов.
В Common Lisp’е некоторые буквы используется в определённых целях. Полное описание синтаксиса можно прочесть в главе 22, но небольшой обзор здесь может быть также полезен:
Квадратные и фигурные скобки, вопросительный и восклицательные знаки, ([, ], {, }, ? и !) не используются ни для каких целей в стандартном синтаксисе Common Lisp. Эти символы явно зарезервированы для пользователей, преимущественно для использования в качестве макро-символов для пользовательских расширений синтаксиса (см. раздел 22.1.3).
Весь код в данной книге записан в нижнем регистре. Common Lisp не чуствителен к регистру текста программы. Внутри при обработке Common Lisp транслирует все имена символов в верхний регистр. С помощью *print-case* можно настроить регистр вывода имен символов. В данной книге, там где отображается взаимодействие пользователя и Lisp’овой системы, ввод представлен в нижнем регистре, а вывод в верхнем.
Настройка переменной readtable-case позволяет именам символов быть регистрозависимыми. Однако, поведение по умолчанию такое, как описано в предыдущем параграфе. В любом случае, описанные в книге имена символов внутри представлены с помощью букв в верхнем регистре.