
- •Оглавление
- •1.Введение
- •2.Знакомство с реализацией Лиспа
- •2.1.Интерактивное программирование
- •2.2.Эксперименты в repl
- •3.Практикум: Простая база данных
- •3.1.Cd и Записи
- •3.2.Заполнение cd
- •3.3.Просмотр содержимого базы данных
- •3.4.Улучшение взаимодействия с пользователем
- •3.5.Сохранение и загрузка базы данных
- •3.6.Выполнение запросов к базе данных
- •3.7.Обновление существующих записей
- •3.8.Избавление от дублирующего кода
- •3.9.Об упаковке
- •4.Синтаксис и семантика
- •4.1.Зачем столько скобок?
- •4.2.Разделение черного ящика
- •4.5.Вызовы функций
- •4.6.Специальные операторы
- •4.7.Макросы
- •4.8.Истина, Ложь и Равенство
- •4.9.Форматирование кода Lisp
- •5.Функции
- •5.1.Определение новых функций
- •5.2.Списки параметров функций
- •5.3.Необязательные параметры
- •5.4.Остаточные (Rest) параметры
- •5.5.Именованные параметры
- •5.6.Совместное использование разных типов параметров
- •5.7.Возврат значений из функции
- •5.8.Функции как данные или Функции высшего порядка
- •5.9.Анонимные функции
- •6. Переменные
- •6.1.Основы переменных
- •6.2.Лексические переменные и замыкания
- •6.3.Динамические (специальные) переменные
- •6.4.Константы
- •6.5.Присваивание
- •6.6.Обобщенное присваивание
- •6.7.Другие способы изменения "мест"
4.5.Вызовы функций
Правило вычисления для форм вызова функции просто: вычисление элементов списка, начиная со второго, как форм Lisp и передача результатов в функцию, именованную первым элементом. Это правило явно добавляет несколько дополнительных синтаксических ограничений на форму вызова функции: все элементы списка после первого должны также быть правильными формами Lisp. Другими словами, базовый синтаксис формы вызова функции следующий (каждый аргумент также является формой Lisp):
(function-name argument*)
Таким образом, следующее выражение вычисляется путем первоначального вычисления 1, затем 2, а затем передачи результатов вычислений в функцию +, которая возвращает 3:
(+ 1 2)
Более сложное выражение, такое как следующее, вычисляется схожим образом за исключением того, что вычисление аргументов (+ 1 2) и (- 3 4) влечет за собой вычисление аргументов этих форм и применение соответствующих функций к ним:
(* (+ 1 2) (- 3 4))
В итоге, значения 3 и -1 передаются в функцию *, которая возвращает -3.
Как показывают эти примеры, функции используются для многих вещей, которые требуют специального синтаксиса в других языках. Это помогает сохранять синтаксис Lisp регулярным.
4.6.Специальные операторы
Нужно сказать, что не все операции могут быть определены как функции. Так как все аргументы функции вычисляются перед ее вызовом, не существует возможности написать функцию, которая ведет себя как оператор IF, который вы использовали в главе 3. Для того, чтобы увидеть почему, рассмотрим такую форму:
(if x (format t "yes") (format t "no"))
Если IF является функцией, процедура вычисления будет вычислять аргументы выражения слева направо. Символ x будет вычислен как переменная, возвращающая свое значение; затем как вызов функции будет вычислена (format t "yes"), возвращающая NIL после печати "yes" на стандартный вывод; и затем будет вычислена (format t "no"), печатающая "no" и возвращающая NIL. Только после того, как эти три выражения будут вычислены, их результаты будут переданы в IF, слишком поздно для того, чтобы проконтролировать, какое из двух выражений FORMAT будет вычислено.
Для решения этой проблемы Common Lisp определяет небольшое количество так называемых специальных операторов (и один из них IF), которые делают те вещи, которые функции сделать не могут. Всего их 25, но только малая их часть напрямую используется в ежедневном программировании13).
Если первый элемент списка является символом, именующим специальный оператор, остальная часть выражения вычисляется в соответствии с правилом для этого оператора.
Правило для IF очень просто: вычисление первого выражения. Если оно вычисляется не в NIL, то вычисляется следующее выражение и возвращается его результат. Иначе возвращается значение вычисления третьего выражения или NIL, если третье выражение не задано. Другими словами, базовая форма выражения IF следующая:
(if test-form then-form [ else-form ])
test-form вычисляется всегда, а затем только одна из then-form и else-form.
Еще более простой специальный оператор - это QUOTE, который получает одно выражение как аргумент и просто возвращает его не вычисляя. Например, следующая форма вычисляется в список (+ 1 2), а не в значение 3:
(quote (+ 1 2))
Этот список не отличается ни от какого другого, вы можете манипулировать им также, как и любым другим, который вы можете создать с помощью функции LIST14)
QUOTE используется достаточно часто, поэтому для него в процедуру чтения был встроен специальный синтаксис. Вместо написания такого:
(quote (+ 1 2))
вы можете написать это:
'(+ 1 2)
Этот синтаксис является небольшим расширением синтаксиса s-выражений, понимаемым процедурой чтения. С этой точки зрения для процедуры вычисления оба этих выражения выглядят одинаково: список, чей первый элемент является символом QUOTE, а второй элемент – список (+ 1 2)15).
В общем, специальные операторы реализуют возможности языка, которые требуют специальной обработки процедурой вычисления. Например, некоторые специальные операторы манипулируют окружением, в котором вычисляются другие формы. Один из них, который я обсужу детально в главе 6, - LET, который используется для создания новой привязки переменной (variable binding). Следующая форма вычисляется в 10, так как второй x вычисляется в окружении, где он именует переменную, связанную оператором LET со значением 10:
(let ((x 10)) x)