
- •Оглавление
- •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.Другие способы изменения "мест"
6.6.Обобщенное присваивание
Привязки переменных, конечно, не являются единственными "местами", которые могут содержать значения. Common Lisp поддерживает составные структуры данных, такие как массивы, хэш-таблицы, списки, а также определенные пользователем структуры данных — такие структуры состоят из множества "мест", способных содержать значения.
Я опишу эти структуры данных в последующих главах, но так как мы рассматриваем присваивание, вы должны знать, что SETF может присвоить значение любому "месту". Когда я буду описывать различные составные структуры данных, я буду указывать, какие функции могут использоваться как "места, обрабатываемые SETF" ("SETFable places"). Кратко же можно сказать, что если вам нужно присвоить значение "месту", почти наверняка следует использовать SETF. Возможно даже расширить SETF для того, чтобы он мог осуществлять присваивание определенным пользователем "местам", хотя я не описываю такие возможности16).
В этом отношении SETF не отличается от оператора присваивания = языков, произошедших от C. В этих языках оператор = присваивает новые значения переменным, элементам массивов, полям классов. В языках, таких как Perl и Python, которые поддерживают хэш-таблицы как встроенные типы данных, = может также устанавливать значения элементов хэш-таблицы. Таблица 6-1 резюмирует различные способы, которыми используется = в этих языках.
Таблица 6-1. Присваивание с помощью = в других языках программирования
Присваивание ... |
Java, C, C++ |
Perl |
Python |
... переменной |
x = 10; |
$x = 10; |
x = 10 |
... элементу массива |
a[0] = 10; |
$a[0] = 10; |
a[0] = 10 |
... элементу хэш-таблицы |
– |
$hash{'key'} = 10; |
hash['key'] = 10 |
... полю объекта |
o.field = 10; |
$o->{'field'} = 10; |
o.field = 10 |
SETF работает сходным образом: первый "аргумент" SETF является "местом" для хранения значения, а второй предоставляет само значения. Как и с оператором = в этих языках, вы используете одинаковую форму и для выражения "места", и для получения значения17). Таким образом, эквиваленты вышеприведенных в таблице 6-1 присваиваний для Lisp следующие (AREF — функция доступа к массиву, GETHASH осуществляет операцию поиска в хэш-таблице, а field может быть функцией, которая обращается к слоту под именем field определенного пользователем объекта):
Простая переменная: (setf x 10) Массив: (setf (aref a 0) 10) Хэш-таблица: (setf (gethash 'key hash) 10) Слот с именем 'field': (setf (field o) 10)
Обратите внимание, что присваиваение с помощью SETF "месту", которое является частью большего объекта, имеет ту же семантику, что и присваивание переменной: "место" модифицируется без оказания какого-либо влияния на объект, который хранился там до этого. И вновь, это подобно тому, как ведет себя = в Java, Perl и Python18).