Россия |
Свойства атомов и работа с памятью
Деструктивные (разрушающие) операции
Идеальный Лисп универсален в смысле теории вычислимых функций от символьных выражений. Но для практичности системы программирования Лиспу требуется дополнительный инструмент, увеличивающий выразительную силу и эффективность языка.
В частности, идеальный Лисп не имеет возможности модифицировать структуру списка. Единственная базисная функция, влияющая на структуру списка - это cons, а она не изменяет существующие списки, а создает все новые и новые. Функции, описанные в чистом Лиспе, такие как subst, в действительности не модифицируют свои аргументы, но делают модифицированную копию оригинала.
Идеальный Лисп работает как расширяемая система, в которой информация как бы никогда не пропадает. Set внутри Prog лишь формально смягчает это свойство, сохраняя ассоциативный список и моделируя присваивания теми же CONS.
Теперь же Лисп обобщается с точки зрения структуры списка добавлением разрушающих средств - деструктивных базисных операций над списками rplaca и rplacd. Эти операции могут применяться для замены адреса или декремента любого узла в списке подобно стандартным присваиваниям. Они используются ради их воздействия на память и относятся к категории псевдо-функций.
(rplaca x y) заменяет адресную часть x на y. Ее значение - x, но x, отличное от того, что было раньше. На языке значений rplaca можно описать равенством
(rplaca x y) = (cons y (cdr x))
Но действие совершенно различно: никакие cons не вызываются и новые слова не создаются.
(rplacd x y) заменяет декремент x на y.
Деструктивные операции должно применять с осторожностью! Они могут совершенно преобразить существующие определения и основную память. Их применение может породить циклические списки, возможно, влекущие бесконечную печать или выглядящие бесконечными для таких функций как equal и subst.
Такие функции используются при реализации списков свойств атома и ряда эффективных, но небезопасных, функций Clisp-а, таких как nconc, mapc и т.п.
Для примера вернемся к функции grp. Это преобразующая список функция, которая преобразует копию своего аргумента, реорганизуя подструктуру
в структуру из тех же атомов:
Выше приведенное определение функции делает это созданием новой структуры и использует для этого четыре cons. Из-за того, что в оригинале только три слова, по крайней мере один cons необходим, а grp можно переписать с помощью rplaca и rplacd.
Изменение состоит в следующем:
Пусть новое машинное слово строится как (cons (cadr x) (cddr x)). Тогда указатель на него заготавливает форма:
(rplaca (cdr x) (cons (cadr x) (cddr x)))
Другое изменение состоит из удаления указателя из второго слова на третье. Оно делается как (rplaca (cdr x) NIL).
Новое определение функции pgrp можно определить как соотношение:
(pgrp x)=(rplacd(rplaca(cdr x)(cons(cadr x)(cddr )x)))NIL)
Функция pgrp используется в сущности ради ее действия. Ее значением, неиспользуемым, является подструктура ((B C)). Поэтому необходимо, чтобы pgrp выполнялось, а ее значение игнорировалось.
Расширенный деструктивными функциями Лисп хорошо приспособлен к оптимизации программ. Любые совпадающие подвыражения можно локализовать и вынести за скобки.
"Сборка мусора" - повторное распределение памяти
Самым интересным, можно сказать революционным, механизмом работы с памятью в Лиспе бесспорно явилась "сборка мусора". С начала 60-ых годов методам такой работы посвящены многочисленные исследования, продолжающиеся до наших дней и сильно активизировавшиеся в связи с включением похожего механизма в реализацию языка Java.
Общая идея всех таких методов достаточно проста:
- пока памяти хватает, о ней можно не беспокоиться и располагать новые данные в новых блоках памяти,
- если памяти вдруг не оказалось, то надо выполнить "сборку мусора", при которой можно найти блоки, ставшие бесполезными для программы,
- если память нашлась, ее снова можно беззаботно тратить.
Специальная программа "сборка мусора" выполняет анализ достижимости всех блоков памяти просто пометкой узлов, видимых из конечного числа рабочих регистров системы программирования. К таким регистрам относятся промежуточные результаты вычислений, активная часть стека, ассоциативный список, таблица атомов и др. После пометки все непомеченные узлы объединяются в список свободной памяти, передающий их для повторного использования новым вызовам функции CONS. Такая автоматизация не лишена недостатков, но они обнаруживаются лишь в сравнительно сложных процессах, требования которых мы сейчас сознательно не рассматриваем.
Выводы:
- Списки свойств атомов обеспечивают прямой доступ к значениям и определениям, а также к произвольным свойствам, как встроенным, так и программируемым.
- Деструктивные операции могут повысить эффективность вычислений ценой надежности программирования.
- Автоматическое перераспределение памяти позволяет программисту не отвлекаться от решения своих задач на технические проблемы, связанные с планированием структур данных.