Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Pol_Grem_-_ANSI_Common_Lisp_High_tech_-_2012.pdf
Скачиваний:
28
Добавлен:
12.03.2016
Размер:
4.85 Mб
Скачать

12

Структура

В разделе 3.3 было­ объяс­не­но,­ каким­ обра­зом­ исполь­­зова­ние­ указа­те­лей­ позво­ля­ет­ помес­тить­ любое­ значе­ние­ куда­ угодно­. Это утвер­жде­ние­ включа­ет­ массу­ возмож­но­стей,­ но не все они могут­ принес­ти­ пользу­. На­ пример,­ объ­ект может­ быть элемен­том­ само­го­ себя­. Хоро­шо­ это или пло­ хо, зави­сит­ от того,­ дела­ет­ся­ это наме­рен­но­ или проис­хо­дит­ случай­но­.

12.1. Разделяемая структура

Списки­ могут­ совме­ст­но­ исполь­зо­вать­ одни­ и те же ячейки­. В простей­­ шем случае­ один список­ может­ быть частью­ друго­го­. После­ выпол­не­ния­

>(setf part (list ’b ’c)) (B C)

>(setf whole (cons ’a part)) (A B C)

первая­ ячейка­ стано­вит­ся­ частью­ (а точнее,­ cdr) второй­. В подоб­ных­ случа­ях­ приня­то­ гово­рить,­ что два списка­ разде­ля­ют­ одну­ структу­ру­. Структу­ра,­ лежа­щая­ в осно­ве­ двух таких­ списков,­ представ­ле­на­ на рис. 12.1.

part =

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

whole =

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

nil

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

a

 

 

 

 

b

 

 

 

 

c

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Рис. 12.1. Разде­ляе­мая­ структу­ра­

12.1. Разделяемая структура

203

Подоб­ные­ ситуа­ции­ выяв­ля­ет­ преди­кат­ tailp. Он прини­ма­ет­ два списка­ и возвра­ща­ет­ исти­ну,­ если­ встретит­ первый­ список­ при обхо­де­ второ­го­.

> (tailp part whole) T

Мы можем­ реали­зо­вать­ его само­стоя­тель­но:­

(defun our-tailp (x y) (or (eql x y)

(and (consp y)

(our-tailp x (cdr y)))))

Соглас­но­ этому­ опре­де­ле­нию­ каждый­ список­ явля­ет­ся­ хвостом­ само­го­ себя,­ а nil явля­ет­ся­ хвостом­ любо­го­ правиль­но­го­ списка­.

В более­ сложном­ случае­ два списка­ могут­ разде­лять­ общую­ структу­ру,­ даже­ когда­ один из них не явля­ет­ся­ хвостом­ друго­го­. Это проис­хо­дит,­ когда­ они делят­ общий­ хвост, как пока­за­но­ на рис. 12.2. Созда­дим­ по­ добную­ ситуа­цию:­

(setf part (list ’b ’c) whole1 (cons 1 part) whole2 (cons 2 part))

Теперь­ whole1 и whole2 разде­ля­ют­ структу­ру,­ но один не явля­ет­ся­ хво­ стом друго­го­.

part =

 

 

 

 

 

 

 

 

 

 

 

 

 

 

whole1 =

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

whole2 =

 

 

 

1

 

 

 

 

 

 

 

 

 

 

nil

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

b

 

 

 

 

c

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

2

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Рис. 12.2. Разде­ляе­мый­ хвост

В случае­ вложен­ных­ списков­ важно­ отли­чать­ списки­ с разде­ляе­мой­ структу­рой­ от их элемен­тов­ с разде­ляе­мой­ структу­рой­. Структу­ра­ спи­ ска верхне­го­ уровня­ включа­ет­ ячейки,­ из кото­рых­ состо­ит­ сам список,­ но не включа­ет­ какие­-либо­ ячейки,­ из кото­рых­ состо­ят­ отдель­ные­ эле­ менты­ списка­. Пример­ структу­ры­ верхне­го­ уровня­ вложен­но­го­ списка­ приве­ден­ на рис. 12.3.

Имеют­ ли две ячей­ки разде­ляе­мую­ структу­ру,­ зави­сит­ от того,­ счита­ем­ мы их списка­ми­ или деревь­я­ми­. Два вложен­ных­ списка­ могут­ разде­­ лять одну­ структу­ру­ как дере­вья,­ но не разде­лять­ ее как списки­. Сле­

204

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Глава 12. Структура

дующий­ код созда­ет­ ситуа­цию,­ изобра­жен­ную­ на рис. 12.4, где два спи­

ска содер­жат­ один и тот же элемент­-список:­

 

 

 

 

 

 

 

(setf element

(list

’a ’b)

 

 

 

 

 

 

 

holds1

(list

1 element 2)

 

 

 

 

 

 

 

holds2

(list

element 3))

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

nil

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

a

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

d

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

nil

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

bc

Рис. 12.3. Структу­ра­ списка­ верхне­го­ уровня­

holds1 =

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

nil

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1

 

 

 

 

 

 

 

2

 

 

 

element =

 

 

 

 

 

 

 

 

 

 

nil

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

a

 

 

 

 

b

 

 

 

 

 

holds2 =

 

 

 

 

 

 

 

nil

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

3

Рис. 12.4. Разде­ляе­мое­ подде­ре­во­

Хотя­ второй­ элемент­ holds1 разде­ля­ет­ структу­ру­ с первым­ элемен­том­ holds2 (в дейст­ви­тель­но­сти,­ он ему иденти­чен),­ holds1 и holds2 не делят­ между­ собой­ общую­ структу­ру­ как списки­. Два списка­ разде­ля­ют­ струк­ туру­ как спи­ски, только­ если­ они делят­ общую­ структу­ру­ верхне­го­ уровня,­ чего­ не дела­ют­ holds1 и holds2.

Избе­жать­ исполь­зо­ва­ния­ разде­ляе­мой­ структу­ры­ можно с помощью ко­ пиро­вания­. Функция­ copy-list, опре­де­ляе­мая­ как

(defun our-copy-list (lst) (if (null lst)

nil

(cons (car lst) (our-copy-list (cdr lst)))))

12.2. Модификация

205

вернет­ список,­ не разде­ляю­щий­ структу­ру­ верхне­го­ уровня­ с исход­ным­ списком­. Функция­ copy-tree, кото­рая­ может­ быть опре­де­ле­на­ следую­­ щим обра­зом:­

(defun our-copy-tree (tr) (if (atom tr)

tr

(cons (our-copy-tree (car tr)) (our-copy-tree (cdr tr)))))

возвра­тит­ список,­ кото­рый­ не разде­ля­ет­ структу­ру­ всего­ дере­ва­ с исход­­ ным списком­. Рисунок 12.5 демон­ст­ри­ру­ет­ разни­цу­ между­ вызо­вом­ co­ py-list и copy-tree для вложен­но­го­ списка­.

 

x

(copy list x)

(copy tree x)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

nil

 

 

nil

 

 

 

 

 

nil

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

nil

 

 

 

 

 

 

 

 

nil

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

a

Рис. 12.5. Два спосо­ба­ копи­ро­ва­ния­

12.2. Модификация

Почему стоит­ избе­гать­ исполь­зо­ва­ния­ разде­ляе­мой­ структу­ры?­ До сих пор это явле­ние­ рассмат­ри­ва­лось­ всего­ лишь как забав­ная­ голо­во­лом­­ ка, а в напи­сан­ных­ ранее­ програм­мах­ мы прекрас­но­ обхо­ди­лись­ и без него­. Разде­ляе­мая­ структу­ра­ вызы­­вает­ пробле­мы,­ если­ объек­ты,­ кото­­ рые имеют­ такую­ структу­ру,­ моди­фи­ци­ру­ют­ся­. Дело­ в том, что моди­­ фика­ция­ одно­го­ из двух списков­ с общей­ структу­рой­ повле­чет­ за собой­ непред­на­ме­рен­ное­ изме­не­ние­ друго­го­.

В преды­ду­щем­ разде­ле­ мы виде­ли,­ как сделать­ один список­ хвостом­ друго­го:­

(setf whole (list ’a ’b ’c) tail (cdr whole))

Изме­не­ние­ списка­ tail повле­чет­ симмет­рич­ное­ изме­не­ние­ хвоста­ whole, и наобо­рот,­ так как по сути­ это одна­ и та же ячей­ка:

>(setf (second tail) ’e)

E

>tail

(B E)

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]