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

3.16. Мусор

69

3.16. Мусор

Опера­ции­ со списка­ми­ могут­ быть доволь­но­ медлен­­ными­ по ряду­ при­ чин. Доступ­ к опре­де­лен­­ному­ элемен­ту­ списка­ осуще­ст­в­ля­ет­ся­ не непо­­ средст­вен­­но по его номе­ру,­ а путем­ после­до­ва­тель­но­го­ пере­бо­ра­ всех предше­ст­вую­щих­ ему элемен­тов,­ что может­ быть суще­ст­вен­­но медлен­­ нее, особен­но­ для больших­ списков­. Так как список­ явля­ет­ся­ набо­ром­ вложен­ных­ ячеек,­ то для досту­па­ к элемен­ту­ прихо­дит­ся­ выпол­нить­ несколь­ко­ пере­хо­дов­ по указа­те­лям,­ в то время­ как для досту­па­ к эле­ менту­ масси­ва­ доста­точ­но­ просто­ увели­чить­ пози­цию­ указа­те­ля­ на за­ данное­ число­. Впрочем,­ намно­го­ более­ затрат­ным­ может­ быть выде­ле­­ ние новых­ ячеек­.

Авто­ма­ти­че­ское­ управле­ние­ памя­тью­ – одна­ из наибо­лее­ ценных­ осо­ бенно­стей­ Лиспа­. Лисп-систе­ма­ рабо­та­ет­ с сег­ментом­ памя­ти,­ назы­вае­­ мым куча­ (heap). Систе­ма­ владе­ет­ инфор­ма­ци­ей­ об исполь­­зован­ной­ и не­ исполь­­зован­ной­ памя­ти­ и выде­ля­ет­ послед­нюю­ для разме­ще­ния­ новых­ объек­тов­. Напри­мер,­ функция­ cons выде­ля­ет­ память­ под созда­вае­мую­ ею ячейку,­ поэто­му­ созда­ние­ новых­ объек­тов­ часто­ назы­ва­ют­ consing.

Если­ память­ будет­ выде­лять­ся,­ но не осво­бо­ж­дать­ся,­ то рано­ или позд­ но свобод­ная­ память­ закон­чит­ся,­ и Лисп прекра­тит­ рабо­ту­. Поэто­му­ не­ обхо­дим­ меха­низм­ поис­ка­ и осво­бо­ж­де­ния­ участ­ков­ кучи,­ кото­рые­ бо­ лее не содер­жат­ нужных­ данных­. Память,­ кото­рая­ стано­вит­ся­ ненуж­­ ной, назы­­вает­ся­ мусо­ром­ и подле­жит­ уборке­. Этот процесс­ назы­­вает­ся­

сборкой­ мусо­ра­ (garbge collection, GC).

Как появ­ля­ет­ся­ мусор?­ Давай­те­ немно­го­ наму­со­рим:­

>(setf lst (list ’a ’b ’c)) (A B C)

>(setf lst nil)

NIL

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

Объек­ты,­ кото­рые­ ника­ким­ обра­зом­ не могут­ быть доступ­ны­ для исполь­­ зова­ния,­ и счита­ют­ся­ мусо­ром­. Теперь­ систе­ма­ может­ безбо­яз­нен­но­ по­ вторно­ исполь­зо­вать­ память,­ выде­лен­­ную cons, по своему­ усмот­ре­нию­.

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

1Это не всегда­ так. В toplevel доступ­ны­ глобаль­ные­ пере­мен­ные­ *, **, ***, кото­­ рым авто­ма­ти­че­ски­ присваи­ва­ют­ся­ послед­ние­ три вычис­лен­ных­ значе­ния­. До тех пор пока­ значе­ние­ связа­но­ с одной­ из выше­упо­мя­ну­тых­ пере­мен­ных,­ мусо­ром­ оно не счита­ет­ся­.

70

Глава 3. Списки

ректным­ управле­ни­ем­ памя­тью­. Утечки­ памя­ти­ и вися­чие­ указа­те­ли­ просто­ невоз­мож­ны­ в Лиспе­.

Разу­ме­ет­ся,­ за удобст­во­ надо­ платить­. Авто­ма­ти­че­ское­ управле­ние­ па­ мятью­ рабо­та­ет­ во вред неак­ку­рат­но­му­ програм­ми­сту­. Затра­ты­ на рабо­­ ту с кучей­ и уборку­ мусо­ра­ иногда­ списы­ва­ют­ на consing. Это имеет­ под собой­ осно­ва­ния,­ посколь­ку,­ за исклю­че­ни­ем­ случа­ев,­ когда­ програм­ма­ ниче­го­ не выбра­сы­ва­ет,­ большин­ст­во­ выде­лен­ных­ cons-ячеек­ в конце­ концов­ окажут­ся­ мусо­ром­. И беда­ в том, что рабо­та­ с consing может­ быть доволь­но­ затрат­ной­ по сравне­нию­ с обычны­ми­ опера­ция­ми­ в програм­­ ме. Конеч­но,­ прогресс­ не стоит­ на месте,­ авто­ма­ти­че­ское­ управле­ние­ памя­тью­ стано­вит­ся­ более­ эффек­тив­ным­ и алго­рит­мы­ по сборке­ мусо­ра­ совер­шен­ст­ву­ют­ся,­ но consing всегда­ будет­ иметь опре­де­лен­­ную стои­ мость, доволь­но­ суще­ст­вен­­ную в неко­то­рых­ реали­за­ци­ях­ язы­ка.

Буду­чи­ неак­ку­рат­ным,­ легко­ напи­сать­ програм­му,­ выде­ляю­щую­ чрез­ мерно­ много­ памя­ти­. Напри­мер,­ remove копи­ру­ет­ ячейки,­ созда­вая­ но­ вый список,­ чтобы­ не вызвать­ побоч­ный­ эффект­. Тем не менее­ тако­го­ копи­ро­ва­ния­ можно­ избе­жать,­ исполь­зуя­ дест­рук­тив­ные­ опера­ции,­ кото­рые­ моди­фи­ци­ру­ют­ суще­ст­вую­щие­ данные,­ а не созда­ют­ новые­. Дест­рук­тив­ные­ функции­ подроб­но­ рассмот­ре­ны­ в разделе 12.4.

Тем не менее­ возмож­но­ напи­са­ние­ программ,­ кото­рые­ вооб­ще­ не выде­­ ляют­ память­ в процес­се­ выпол­не­ния­. Часто­ програм­ма­ снача­ла­ пишет­­ ся в чисто­ функцио­наль­ном­ стиле­ с исполь­­зова­ни­ем­ большо­го­ коли­че­ст­­ ва списков,­ а по мере­ разви­тия­ копи­рую­щие­ функции­ заме­ня­ют­ся­ на дест­рук­тив­ные­ в крити­че­ских­ к произ­во­ди­тель­но­сти­ участ­ках­ кода­. Но в этом случае­ сложно­ давать­ конкрет­ные­ сове­ты,­ пото­му­ что неко­то­рые­ совре­мен­ные­ реали­за­ции­ Лиспа­ управля­ют­ памя­тью­ настоль­ко­ хоро­­ шо, что иногда­ выде­ле­ние­ новой­ памя­ти­ может­ быть эффек­тив­нее,­ чем исполь­­зова­ние­ уже суще­ст­вую­щей­. Более­ деталь­но­ этот вопрос­ рассмат­­ рива­ет­ся­ в разде­ле­ 13.4.

Во всяком­ случае,­ consing подхо­дит­ для прото­ти­пов­ и экспе­ри­мен­тов­. А если­ вы восполь­зуе­тесь­ гибко­стью,­ кото­рую­ дают­ списки,­ при изна­­ чальном­ напи­са­нии­ програм­мы,­ то у вас больше­ шансов,­ что она дожи­­ вет до более­ поздних­ этапов,­ на кото­рых­ пона­до­бит­ся­ опти­ми­за­ция­.

Итоги главы

1.Cons-ячей­ка – это структу­ра,­ состоя­щая­ из двух объек­тов­. Списки­ состо­ят­ из связан­ных­ между­ собой­ ячеек­.

2.Преди­кат­ equal менее­ строг, чем eql. Факти­че­ски­ он возвра­ща­ет­ ис­ тину,­ если­ объек­ты­ печа­та­ют­ся­ одина­ко­во­.

3.Все объек­ты­ в Лиспе­ ведут­ себя­ как указа­те­ли­. Вам нико­гда­ не при­ дется­ управлять­ указа­те­ля­ми­ явно­.

4.Скопи­ро­вать­ список­ можно­ с помо­щью­ copy-list, объ­единить­ два спи­ ска – с помо­щью­ append.

Упражнения

71

5.Коди­ро­ва­ние­ повто­ров ­– простой­ алго­ритм­ сжатия­ списков,­ легко­ реали­зуе­мый­ в Лиспе­.

6.Common Lisp имеет­ бога­тый­ набор­ средств для досту­па­ к элемен­там­ списков,­ и эти функции­ опре­де­ле­ны­ в терми­нах­ car и cdr.

7.Отобра­жаю­щие­ функции­ приме­ня­ют­ опре­де­лен­­ную функцию­ по­ следо­ва­тель­но­ к каждо­му­ элемен­ту­ или каждо­му­ хвосту­ списка­.

8.Опера­ции­ с вложен­ны­ми­ списка­ми­ сродни­ опера­ци­ям­ с бинар­ны­ми­ деревь­я­ми­.

9.Чтобы­ оценить­ коррект­ность­ рекур­сив­ной­ функции,­ доста­точ­но­ убе­ диться,­ что она соот­вет­ст­ву­ет­ несколь­ким­ требо­ва­ни­ям­.

10.Списки­ могут­ рассмат­ри­вать­ся­ как множе­ст­ва­. Для рабо­ты­ с множе­­ ства­ми­ в Лиспе­ есть ряд встроен­ных­ функций­.

11.Аргу­мен­ты­ по ключу­ не явля­ют­ся­ обяза­тель­ны­ми­ и опре­де­ля­ют­ся­ не поло­же­ни­ем,­ а по соот­вет­ст­вую­щей­ метке­.

12.Список ­– подтип­ после­до­ва­тель­но­сти­. Common Lisp имеет­ множе­ст­­ во функций­ для рабо­ты­ с после­до­ва­тель­но­стя­ми­.

13.Cons-ячей­ка, не являю­щая­ся­ правиль­ным­ списком,­ назы­­вает­ся­ то­ чечной­ парой­.

14.С помо­щью­ списков­ точеч­ных­ пар можно­ предста­вить­ элемен­ты­ ото­ браже­ния­. Такие­ списки­ назы­­вают­ся­ ассо­циа­тив­ны­ми­.

15.Авто­ма­ти­че­ское­ управле­ние­ памя­тью­ осво­бо­ж­да­ет­ програм­ми­ста­ от ручно­го­ выде­ле­ния­ памя­ти,­ но большое­ коли­че­ст­во­ мусо­ра­ может­ замед­лить­ рабо­ту­ програм­мы­.

Упражнения

1.Представь­те­ следую­щие­ списки­ в виде­ ячеек:­

(a)(a b (c d))

(b)(a (b (c (d))))

(c)(((a b) c) d)

(d)(a (b . c) . d)

2.Напи­ши­те­ свой вари­ант­ функции­ union, кото­рый­ сохра­ня­ет­ поря­док­ следо­ва­ния­ элемен­тов­ соглас­но­ исход­ным­ спискам:­

>(new-union ’(a b c) ’(b a d)) (A B C D)

3.Напи­ши­те­ функцию,­ опре­де­ляю­щую­ коли­че­ст­во­ повто­ре­ний­ (с точ­ ки зрения­ eql) каждо­го­ элемен­та­ в задан­ном­ списке­ и сорти­рую­щую­ их по убыва­нию­ встречае­мо­сти:­

>(occurrences ’(a b a d a c d c a))

((A . 4) (C . 2) (D . 2) (B . 1))

4. Поче­му­ (member ’(a) ’((a) (b))) возвра­ща­ет­ nil?

72

Глава 3. Списки

5.Функция­ pos+ прини­ма­ет­ список­ и возвра­ща­ет­ новый,­ каждый­ эле­ мент кото­ро­го­ увели­чен­ по сравне­нию­ с исход­ным­ на его поло­же­ние­ в списке:­

> (pos+ ’(7 5 1 4)) (7 6 3 7)

Опре­де­ли­те­ эту функцию­ с помо­щью:­ (a) рекур­сии,­ (b) итера­ции­ и

(c)mapcar.

6.После­ долгих­ лет разду­мий­ госу­дар­ст­вен­ная­ комис­сия­ приня­ла­ по­ станов­ле­ние,­ соглас­но­ кото­ро­му­ cdr указы­ва­ет­ на первый­ элемент­ списка,­ а car – на его оста­ток­. Опре­де­ли­те­ следую­щие­ функции,­ удов­ летво­ряю­щие­ этому­ поста­нов­ле­нию:­

(a)cons

(b)list1

(c)length (для списков)­

(d)member (для списков,­ без ключе­вых­ пара­мет­ров)­

7.Изме­ни­те­ програм­му­ на рис. 3.6 таким­ обра­­зом, чтобы­ она созда­ва­ла­ меньшее­ коли­че­ст­во­ ячеек­. (Подсказ­ка:­ исполь­зуй­те­ точеч­ные­ пары­.)

8.Опре­де­ли­те­ функцию,­ печа­таю­щую­ задан­ный­ список­ в точеч­ной­ но­ тации:­

> (showdots ’(a b c)) (A . (B . (C . NIL))) NIL

9.Напи­ши­те­ програм­му,­ кото­рая­ ищет наибо­лее­ длинный­ путь в сети,­ не содер­жа­щий­ повто­ре­ний­ (раздел 3.15). Сеть может­ содер­жать­ цик­ лы.

1Зада­чу­ 6b пока­ что не полу­чит­ся­ решить­ с помо­щью­ имеющих­ся­ у вас зна­ ний. Вам потре­бу­ет­ся­ ос­таточ­ный­ аргу­мент­ (&rest), кото­рый­ вводит­ся­ на стр. 114. На эту оплош­ность­ указал­ Рикар­до­ Феррей­ро­ де Оливье­ра­. – Прим. перев­.

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