
248
Глава 3. Модульность, объекты и состояние
если z1 и z2 определены как на рисунках 3.16 и 3.17, (eq? (car z1) (cdr z1))
будет истинно, а (eq? (car z2) (cdr z2)) ложно.
Как будет видно в последующих разделах, с помощью разделения данных мы значи-
тельно расширим репертуар структур данных, которые могут быть представлены через
пары. С другой стороны, разделение сопряжено с риском, поскольку изменения в од-
них структурах могут затрагивать и другие структуры, разделяющие те части, которые
подвергаются изменению. Операции изменения set-car! и set-cdr! нужно использо-
вать осторожно; е сли у нас нет точного понимания, какие из наших объектов разделяют
дан ные, изменение может привести к неожиданным результатам
20
.
Упражнение 3.15.
Нарисуйте стрелочные диаграммы, объясняющие, как set-to-wow! действует на структуры z1
и z2 из этого раздела.
Упражнение 3.16.
Бен Битобор решил написать процедуру для подсчета числа пар в лю бой списковой структуре.
«Это легко, — думает он. — Число пар в любой структуре есть число пар в car плюс число пар в
cdr плюс один на текущую пару». И он пишет следующую процедуру:
(define (count-pairs x)
(if (not (pair? x))
0
(+ (count-pairs (car x))
(count-pairs (cdr x))
1)))
Покажите, что эта процедура ошибочна. В частности, нарисуйте диаграммы, представляющие
списковые структуры ровно из трех пар, для которых Бенова процедура вернет 3; вернет 4; вернет
7; вообще никогда не завершится.
Упражнение 3.17.
Напишите правильную версию процедуры count-pairs из упражнения 3.16, которая возвращает
число различных пар в любой структуре. (Подсказка: просматривайте структуру, поддерживая при
этом вспомогательную структуру, следящую за тем, какие пары уже были посчитаны.)
Упражнение 3.18.
Напишите процедуру, которая рассматривает список и определяет, содержится ли в нем цикл, то
есть, не войдет ли программа, которая попытается добраться до конца списка, продвигаясь по
полям cdr, в бесконечный цикл. Такие списки порождались в упражнении 3.13.
20
Тонкости работы с разделением изменяемых данных отражают сложности с понятием «идентичности» и
«изменения», о которых мы говорили в разделе 3.1.3. Там мы отметили, что введение в наш язык поня-
тия изменения требует, чтобы у составного объекта была «индивидуальность», которая представляет собой
нечто отличное от частей, из которых он состоит. В Лиспе мы считаем, что именно эта «индивидуальность»
проверяется предикатом eq?, то есть сравнением указателей . Поскольку в большинстве реализаций Лиспа
указатель — это, в сущности , адрес в памяти, мы «решаем проблему» определения индивидуальности объ-
ектов, постановив, что «сам» объект данных есть информация, хранимая в некотором наборе ячеек памяти
компьютера. Для простых лисповских программ этого достаточно, но такой метод не способен разрешить
общий вопрос «идентичности» в вычислительных моделях.