[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
В Лиспе атомы записывают в ясной манере; если реализация не ясна на
практике, то она тем не менее ясна в теории. Атом `роза',
например хранится, как четыре последовательные буквы `р',
`о', `з', `а'. С другой стороны, список храниться по
другому. Механизм достаточно простой, но надо чуть привыкнуть к идее.
Список хранится используя набор сдвоенных указателей. В наборе, первый
указатель каждой пары указывает на атом или другой список, а второй
указатель каждой пары указывает на следующую пару указателей, или на
символ nil
, который означает конец списка.
Сам указатель --- это просто электронный адрес того, на что он указывает. Следовательно список хранится, как серия электронных адресов.
Например, в списке (роза фиалка лютик)
--- три элемента,
`роза', `фиалка' и `лютик'. В компьютере электронный
адрес `роза' записывается в сегменте памяти компьютера вместе с
адресом, который является электронным адресом размещения атома
`фиалка'; а этот адрес (тот, который сообщает где размещается
`фиалка') хранится вместе с адресом, который сообщает где
размещен атом `лютик'.
Все это звучит сложнее, чем есть на самом деле, и это легко понять посмотрев на диаграмму:
___ ___ ___ ___ ___ ___ |___|___|--> |___|___|--> |___|___|--> nil | | | | | | --> роза --> фиалка --> лютик |
На диаграмме, каждый прямоугольник представляет слово в памяти
компьютера, которое содержит объект Лиспа, обычно в форме электронного
адреса. Прямоугольники, то есть адреса расположены попарно. Каждая
стрелка указывает туда, где хранится объект с этим адресом --- это
или атом или пара адресов. Первый прямоугольник --- это
электронный адрес атома `роза' и стрелка указывает на атом
`роза'; второй прямоугольник это адрес следующей пары
прямоугольников, первый из которых --- это адрес атома
`фиалка', а второй --- это адрес следующей пары адресов.
Самый последний прямоугольник указывает на символ nil
, который
означает конец списка.
Когда переменной присваивается список, например с помощью функции
setq
, на самом деле в этой переменной сохраняется адрес первого
прямоугольника списка. Так вычисление следующего выражения
(setq букет '(роза фиалка лютик)) |
создаст следующую ситуацию:
букет | | ___ ___ ___ ___ ___ ___ --> |___|___|--> |___|___|--> |___|___|--> nil | | | | | | --> роза --> фиалка --> лютик |
В этом случае, символ букет
содержит адрес первой пары
прямоугольников. В самом деле символ букет
состоит из группы
адресов-прямоугольников, один из которых --- адрес печатного слова
`букет', второй --- это адрес определения функции
прикрепленной к символу, если он конечно есть, а третий --- это
адрес первой пары адресов-прямоугольников для списка (роза
фиалка лютик)
, и т.д.
Тот же самый список можно проиллюстрировать и с помощью других прямоугольников:
букет | | -------------- --------------- ---------------- | | car | cdr | | car | cdr | | car | cdr | -->| роза | o------->| фиалка | o------->| лютик | nil | | | | | | | | | | -------------- --------------- ---------------- |
В прошлых разделах книги я предложил, что вы можете представить символ, как шкаф с полками. Определение функции хранится на одной полке, значение переменной на другой, и так далее. То, что мы положили на одну полку можно изменить, не задев содержимое другой полки и наоборот. Фактически то, что хранится на каждой полке --- это адрес значения или определения функции. Это похоже на старый сундук, где хранится карта захороненных сокровищ.
(Кроме имени, определения функции и значения переменной, у символа есть `полочка' для списка свойств в который можно записать другую информацию. Мы не будем обсуждать здесь списки свойств; смотрите section `Property Lists' in The GNU Emacs Lisp Reference Manual.)
Вот и честное представление:
Шкаф Содержимое Полок --------------------- | | | имя символа | букет | | --------------------- | | | определение функции | [нет] | | --------------------- | | | значение переменной | (роза фиалка лютик) | | --------------------- | | | список свойств | [не описан здесь] | | --------------------- |/ \| |
Если символу присваивается CDR списка, то сам список не меняется; символу просто присваивается адрес другого элемента списка. (На жаргоне Лиспа CAR и CDR `не деструктивны'). Так вычисление следующего выражения
(setq цветы (cdr букет)) |
выдаст вот это:
букет цветы | | | ___ ___ | ___ ___ ___ ___ --> | | | --> | | | | | | |___|___|----> |___|___|--> |___|___|--> nil | | | | | | --> роза --> фиалка --> лютик |
Значение цветы
--- это (фиалка лютик)
, или можно
сказать по другому, символ цветы
содержит адрес пары
адресов-прямоугольников, первый из которых содержит адрес атома
фиалка
, а второй содержит адрес атома лютик
.
Пару адресов-прямоугольников называют списочной ячейкой (cons cell) или точечная пара. See section `List Type' in The GNU Emacs Lisp Reference Manual, и section `Dotted Pair Notation' in The GNU Emacs Lisp Reference Manual, за дополнительной информацией о списочных ячейках и точечных парах.
Функция cons
добавляет новую пару адресов перед серией адресов,
как это показано выше. Например, вычисление выражения
(setq букет (cons 'лилия букет)) |
выдаст:
букет цветы | | | ___ ___ ___ ___ | ___ ___ ___ ___ --> | | | | | | --> | | | | | | |___|___|----> |___|___|----> |___|___|---->|___|___|--> nil | | | | | | | | --> лилия --> роза --> фиалка --> лютик |
Однако это не изменило значение символа цветы
, как вы можете
проверить вычислив следующее выражение,
(eq (cdr (cdr букет)) цветы) |
это вернет t
для true.
До тех пор, пока его не изменили, символ цветы
все еще содержит
значение (фиалка лютик)
--- то есть адрес списочной ячейки,
чей первый элемент является адресом атома фиалка
. Также это не
изменило никакую из других списочных ячеек --- они все еще там.
В Лиспе получая CDR списка, вы только получите адрес следующей
списочной ячейки в группе; получив CAR списка, вы получите первый
элемент списка; чтобы добавить новый элемент в список с помощью
cons
, вы добавляете новую списочную ячейку перед списком. Вот в
принципе и все! Основы Лиспа гениально просты!
А на что указывает последний адрес в группе списочных ячеек? Он
указывает на пустой список, nil
.
В общем говоря, когда переменная Лисп устанавливается значение, то это делается через адрес списка, к которому относится эта переменная.
9.1 Упражнения
[ < ] | [ > ] | [ << ] | [ Up ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |
Установите значением символа цветы
список из фиалка
и
лютик
. С помощью cons
добавьте еще цветов в этот список
и назначьте его значением переменной больше-цветов
. Установите
CAR списка цветы
какую-нибудь рыбу. Что сейчас содержит
список больше-цветов
?
[ << ] | [ >> ] | [Top] | [Contents] | [Index] | [ ? ] |