LISP очень простой список вопросов - PullRequest
3 голосов
/ 09 января 2009

Я изучаю шутки, и я довольно новичок в этом, поэтому мне было интересно ...

если я сделаю это:

(defparameter *list-1* (list 1 2))
(defparameter *list-2* (list 2 3))
(defparameter *list-3* (append *list-1* *list-2*))

А потом

(setf (first *list-2*) 1)
*list-3*

Я получу (1 2 1 4)

Я знаю, это потому, что приложение собирается «сохранить ресурсы» и создать новый список для первого блока, но на самом деле просто укажет на второй блок, потому что если я это сделаю:

(setf (first *list-1*) 0)
*list-3*

Я получу (1 2 1 4) вместо более логичного (0 2 1 4)

Итак, мой вопрос, какие другие случаи похожи на это в lisp, как вы, шалости черного пояса, знаете, как справляться с этими вещами, которые не являются интуитивными или последовательными?

Ответы [ 6 ]

4 голосов
/ 09 января 2009

Одна из защитных тактик - избегать разделения структуры.

(defparameter *list-3* (append *list-1* *list-2* '()))

или

(defparameter *list-3* (append *list-1* (copy-list *list-2*)))

Теперь структура нового *list-3* полностью новая, и модификации *list-3* не затронут *list-2* и наоборот.

4 голосов
/ 09 января 2009

Функция append должна сделать копию своего первого аргумента, чтобы избежать изменения существующих структур данных. В результате теперь у вас есть два сегмента списка, которые выглядят как (1 2 ...), но они являются частью разных списков.

В общем, любой список может быть хвостом любого другого списка, но у вас не может быть одного объекта списка, который служит заголовком нескольких списков.

3 голосов
/ 13 января 2009

цитата:

Итак, мой вопрос: какие еще случаи в Lisp похожи, как вы, черные шалости, знаете, как справляться с этими вещами, которые не являются интуитивными или непоследовательными?

Я думаю, что вы немного грубоваты с темой, которая намного больше, чем вы себе представляете. Списки - это довольно сложная концепция в Лиспе, и вы должны понимать, что это не какой-то простой массив. Стандарт предоставляет lot функций для манипулирования списками любым способом. Что вы хотите в этом случае:

(concatenate 'list *list-1* *list-2*)

Итак, почему там также append? Что ж, если вы можете опустить копирование последнего списка, и все задействованные символы по-прежнему возвращают правильные данные, это может значительно повысить производительность как при расчете времени, так и при использовании памяти. append особенно полезен в стиле функционального программирования, который не использует побочные эффекты.

Вместо дальнейшего объяснения минусов, деструктивных и неразрушающих функций и т. Д., Я укажу вам хорошее введение: Практический Common Lisp, Ch. 12 и, для полной справки, Common Lisp Hyperspec , посмотрите главы 14 и 17.

3 голосов
/ 09 января 2009

Вы должны думать о списках в терминах минусов. Когда вы определяете список 1 и список 2, это выглядит так:

(defparameter *list-1* (cons 1 (cons 2 nil)))
(defparameter *list-2* (cons 2 (cons 3 nil)))

Затем, когда вы добавляете:

(defparameter *list-3* (cons 1 (cons 2 *list-2*)))

По сути, конс-ячейка состоит из двух частей; значение (автомобиль) и указатель (CDR). Функция «Добавить» определена так, чтобы не изменять первый список, поэтому он копируется, но затем изменяется последний cdr (обычно ноль), чтобы он указывал на второй список, а не на копию второго списка. Если бы вы хотели уничтожить первый список, вы бы использовали nconc.

Попробуйте это:

(defparameter *list-3* (nconc *list-1* *list-2*))

Затем обратите внимание на значение *list-1*, оно равно (1 2 2 3), как *list-3*.

Общее правило состоит в том, что неразрушающие функции (append) не будут уничтожать существующие данные, в то время как деструктивные функции (nconc) будут уничтожать. Однако то, что делает будущая деструктивная функция ((setf cdr)), не является обязанностью первой неразрушающей функции.

0 голосов
/ 10 января 2009

Итак, мой вопрос: какие еще случаи в Lisp похожи, как вы, черные шалости, знаете, как справляться с этими вещами, которые не являются интуитивными или непоследовательными?

Читая прекрасное руководство? Hyperpsec прямо заявляет:

[...] структура списка каждого из списков, кроме последнего, копируется. Последний аргумент не копируется; он становится cdr последней пары с точками конкатенации предыдущих списков или возвращается напрямую, если нет предыдущих непустых списков.

0 голосов
/ 09 января 2009

Хм, прежде всего мы узнаем, как это работает, так что то, что мы представляем, непоследовательно, имеет смысл.

Что вам нужно сделать, так это найти несколько устаревших диаграмм блоков и указателей, которые я не могу легко нарисовать, но давайте разберемся с этим.

После первого дефараметра у вас есть list-1 , то есть

(1 . 2 . nil)

в точечной записи; list-2 is

(2 . 3 . nil) 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...