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

3.4. Построение списков

53

3.4. Построение списков

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

> (setf x ’(a b c)

y (copy-list x))

(A B C)

Такая­ структу­ра­ ячеек­ пока­за­на­ на рис. 3.5. Собст­вен­­ную функцию­ copy-list опре­де­лим­ так:

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

lst

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

Здесь подра­зу­ме­ва­ет­ся,­ что x и (copy-list x) всегда­ равны­ с точки­ зрения­ equal, а для eql равны,­ только­ если­ x явля­ет­ся­ nil.

Функция­ append – еще одна­ функция­ для рабо­ты­ со списка­ми,­ склеи­ вающая­ между­ собой­ произ­воль­ное­ коли­че­ст­во­ списков:­

> (append ’(a b) ’(c d) ’(e)) (A B C D E)

Функция­ append копи­ру­ет­ все аргу­мен­ты,­ за исклю­че­ни­ем­ послед­не­го­.

3.5. Пример: сжатие

В этом разде­ле­ при­водит­ся­ пример­ разра­бот­ки­ просто­го­ меха­низ­ма­ сжатия­ списков­. Рассмат­ри­вае­мый­ ниже­ алго­ритм­ при­нято­ назы­­вать

коди­ро­ва­ни­ем­ повто­ров­ (run-length encoding). Представь­те­ ситуа­цию:­ в ресто­ра­не­ офици­ант­ обслу­жи­ва­ет­ столик,­ за кото­рым­ сидят­ четы­ре­ посе­ти­те­ля:­

«Что вы буде­те­ зака­зы­­вать?» – спраши­ва­ет­ он.

«При­неси­те­ фирмен­ное­ блюдо,­ пожа­луй­ста», ­– от­веча­ет­ первый­ по­ сети­тель­.

«И мне тоже», ­– гово­рит­ второй­.

«Ну и я за компа­нию», ­– присо­еди­ня­ет­ся­ третий­.

Все смотрят­ на четвер­то­го­ клиен­та­. «А я бы предпо­чел­ кори­ан­д­ро­вое­ суфле», ­– ти­хо произ­но­сит­ он.

Офици­ант­ со вздохом­ разво­ра­чи­ва­ет­ся­ и идет к кухне­. «Три фирмен­­ ных, – кричит­ он пова­ру, ­– и одно­ кори­ан­д­ро­вое­ суфле»­.

На рис. 3.6 пока­зан­ подоб­ный­ алго­ритм­ для списков­. Функция­ compress прини­ма­ет­ список­ из атомов­ и возвра­ща­ет­ его сжатое­ представ­ле­ние:­

> (compress ’(1 1 1 0 1 0 0 0 0 1)) ((3 1) 0 1 (4 0) 1)

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

(defun compress (x) (if (consp x)

(compr (car x) 1 (cdr x)) x))

(defun compr (elt n lst) (if (null lst)

(list (n-elts elt n)) (let ((next (car lst)))

(if (eql next elt)

(compr elt (+ n 1) (cdr lst)) (cons (n-elts elt n)

(compr next 1 (cdr lst)))))))

(defun n-elts (elt n) (if (> n 1)

(list n elt) elt))

Рис. 3.6. Коди­ро­ва­ние­ повто­ров:­ сжатие­

Если­ какой­-либо­ элемент­ повто­ря­ет­ся­ несколь­ко­ раз, он заме­ня­ет­ся­ на список,­ содер­жа­щий­ этот элемент­ и число­ его повто­ре­ний­.

Большая­ часть рабо­ты­ выпол­ня­ет­ся­ рекур­сив­ной­ функци­ей­ compr, кото­­ рая прини­ма­ет­ три аргу­мен­та:­ elt – послед­ний­ встречен­ный­ элемент;­ n – число­ его повто­ре­ний;­ lst – оста­ток­ списка,­ подле­жа­щий­ дальней­шей­ компрес­сии­. Когда­ список­ закан­чи­ва­ет­ся,­ вызы­­вает­ся­ функция­ n-elts, возвра­щаю­щая­ сжатое­ представ­ле­ние­ n элемен­тов­ elt. Если­ первый­ эле­ мент lst по-прежне­му­ равен­ elt, увели­чи­ва­ем­ n и идем дальше­. В про­ тивном­ случае­ мы полу­ча­ем­ сжатое­ представ­ле­ние­ преды­ду­щей­ серии­ одина­ко­вых­ элемен­тов­ и присо­еди­ня­ем­ это к тому,­ что полу­чим­ с помо­­ щью compr от остат­ка­ списка­.

Чтобы­ рекон­ст­руи­ро­вать­ сжатый­ список,­ восполь­зу­ем­ся­ uncompress (рис. 3.7):

> (uncompress ’((3 1) 0 1 (4 0) 1)) (1 1 1 0 1 0 0 0 0 1)

Эта функция­ выпол­ня­ет­ся­ рекур­сив­но,­ копи­руя­ атомы­ и раскры­вая­ списки­ с помо­щью­ list-of:

> (list-of 3 ’ho) (HO HO HO)

В дейст­ви­тель­но­сти,­ нет необ­хо­ди­мо­сти­ исполь­зо­вать­ list-of. Встроен­­ ная функция­ make-list выпол­ня­ет­ ту же рабо­ту,­ одна­ко­ исполь­зу­ет­ ар­ гумен­ты­ по ключу (keyword), кото­рых­ мы пока­ еще не каса­лись­.

Функции­ compress и uncompress, представ­лен­­ные на рис. 3.6 и 3.7, опре­­ деле­ны­ не так, как это сделал­ бы опытный­ програм­мист­. Они неэф­фек­­

3.6. Доступ

55

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

(defun uncompress (lst) (if (null lst)

nil

(let ((elt (car lst))

(rest (uncompress (cdr lst)))) (if (consp elt)

(append (apply #’list-of elt) rest)

(cons elt rest)))))

(defun list-of (n elt) (if (zerop n)

nil

(cons elt (list-of (- n 1) elt))))

Рис. 3.7. Коди­ро­ва­ние­ повто­ров:­ деко­ди­ро­ва­ние­

Загрузка программ

Код в этом разделе впервые претендует на название отдельной программы. Если наша программа имеет достаточно большой размер, удобно сохранять ее текст в файл. Прочитать код из файла можно с помощью load. Если мы сохраним код с рис. 3.6 и 3.7 в файл под названием "compress.lisp", то, набрав в toplevel

(load "compress.lisp")

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

Учтите, что некоторые реализации Лиспа могут использовать расширение ".lsp", а не ".lisp".

3.6. Доступ

Для досту­па­ к частям­ списков­ в Common Lisp имеют­ся­ еще несколь­ко­ функций,­ кото­рые­ опре­де­ля­ют­ся­ с помо­щью­ car и cdr. Чтобы­ полу­чить­ элемент­ с опре­де­лен­­ным индек­сом,­ вызо­вем­ функцию­ nth:

> (nth 0 ’(a b c)) A

56

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

Чтобы­ полу­чить­ n-й хвост списка,­ вызо­вем­ nthcdr:

> (nthcdr 2 ’(a b c))

(C)

Функции­ nth и nthcdr ведут­ отсчет­ элемен­тов­ списка­ с 0. Вооб­ще­ гово­ря,­ в Common Lisp любая­ функция,­ обра­щаю­щая­ся­ к элемен­там­ структур­ данных,­ начи­на­ет­ отсчет­ с нуля­.

Эти две функции­ очень похо­жи,­ и вызов­ nth экви­ва­лен­тен­ вызо­ву­ car от nthcdr. Опре­де­лим­ nthcdr без обра­бот­ки­ возмож­ных­ ошибок:­

(defun our-nthcdr (n lst) (if (zerop n)

lst

(our-nthcdr (- n 1) (cdr lst))))

Функция­ zerop всего­ лишь прове­ря­ет,­ равен­ ли нулю­ ее аргу­мент­. Функция­ last возвра­ща­ет­ послед­нюю­ cons-ячей­ку списка:­

> (last ’(a b c))

(C)

Это не послед­ний­ элемент;­ чтобы­ полу­чить­ послед­ний­ элемент,­ а не по­ следнюю­ ячейку,­ восполь­зуй­тесь­ функци­ей­ car от last.

В Common Lisp для досту­па­ к элемен­там­ с перво­го­ по деся­тый­ выде­ле­ны­ специ­аль­ные­ функции,­ кото­рые­ полу­чи­ли­ назва­ния­ от англий­ских­ по­ рядко­вых­ числи­тель­ных­ (от first до tenth). Обра­ти­те­ внима­ние,­ что от­ счет начи­на­ет­ся­ не с нуля­ и (second x) экви­ва­лен­тен­ (nth 1 x).

Кроме­ того,­ в Common Lisp опре­де­ле­ны­ функции­ типа­ caddr (сокра­щен­­ ный вызов­ car от cdr от cdr). Также­ опре­де­ле­ны­ функции­ вида­ cxr, где x – набор­ всех возмож­ных­ соче­та­ний­ a и d длиной­ до четы­рех­ симво­лов­. За исклю­че­ни­ем­ cadr, кото­рая­ ссыла­ет­ся­ на второй­ элемент,­ не реко­мен­­ дует­ся­ исполь­зо­вать­ подоб­ные­ функции­ в коде,­ так как они затруд­ня­ют­ его чтение­.

3.7. Отображающие функции

Common Lisp опре­де­ля­ет­ несколь­ко­ опера­ций­ для при­мене­ния­ какой­- либо­ функции­ к каждо­му­ элемен­ту­ списка­. Чаще­ всего­ для этого­ ис­ пользу­ет­ся­ mapcar, кото­рая­ вызы­­вает­ задан­ную­ функцию­ поэле­мент­но­ для одно­го­ или несколь­ких­ списков­ и возвра­ща­ет­ список­ резуль­та­тов:­

> (mapcar #’(lambda (x) (+ x 10)) ’(1 2 3))

(11 12 13)

> (mapcar #’list ’(a b c)

’(1 2 3 4)) ((A 1) (B 2) (C 3))

Тут вы можете оставить комментарий к выбранному абзацу или сообщить об ошибке.

Оставленные комментарии видны всем.

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