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

3.11. Последовательности

61

(defun our-member-if (fn lst) (and (consp lst)

(if (funcall fn (car lst) lst

(our-member-if fn (cdr lst)))))

Функция­ adjoin – своего­ рода­ услов­ный­ cons. Она присо­еди­ня­ет­ задан­­ ный элемент­ к списку,­ но только­ если­ его еще нет в этом списке­ (т. е. не member):

>(adjoin ’b ’(a b c)) (A B C)

>(adjoin ’z ’(a b c)) (Z A B C)

Вобщем­ случае­ adjoin прини­ма­ет­ те же аргу­мен­ты­ по ключу,­ что и member­.

Common Lisp опре­де­ля­ет­ основ­ные­ логи­че­ские­ опера­ции­ с множе­ст­ва­­ ми, такие­ как объеди­не­ние,­ пере­се­че­ние,­ допол­не­ние,­ для кото­рых­ опре­­ деле­ны­ соот­вет­ст­вую­щие­ функции:­ union, intersection, set-difference. Эти функции­ рабо­та­ют­ ровно­ с двумя­ списка­ми­ и имеют­ те же аргу­мен­­ ты по ключу,­ что и member.

>(union ’(a b c) ’(c b s)) (A C B S)

>(intersection ’(a b c) ’(b b c)) (B C)

>(set-difference ’(a b c d e) ’(b e)) (A C D)

Посколь­ку­ в множе­ст­вах­ нет тако­го­ поня­тия,­ как упоря­до­че­ние,­ эти функции­ не сохра­ня­ют­ поря­док­ элемен­тов­ в исход­ных­ списках­. Напри­­ мер, вызов­ set-difference из приме­ра­ может­ с тем же успе­хом­ вернуть­

(d c a).

3.11. Последовательности

Списки­ также­ мож­но рассмат­ри­вать­ как после­до­ва­тель­но­сти­ элемен­­ тов, следую­щих­ друг за другом­ в фикси­ро­ван­ном­ поряд­ке­. В Common Lisp поми­мо­ списков­ к по­следо­ва­тель­но­стям­ также­ от­носят­ся­ векто­­ ры. В этом разде­ле­ вы научи­тесь­ рабо­тать­ со списка­ми­ как с после­до­ва­­ тельно­стя­ми­. Более­ деталь­но­ опера­ции­ с после­до­ва­тель­но­стя­ми­ будут­ рассмот­ре­ны­ в разде­ле­ 4.4.

Длина­ после­до­ва­тель­но­сти­ опре­де­ля­ет­ся­ с помо­щью­ length:

> (length ’(a b c)) 3

Ранее­ мы писа­ли­ урезан­ную­ версию­ этой функции,­ рабо­таю­щую­ толь­ ко со списка­ми­.

62

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

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

>(subseq ’(a b c d) 1 2)

(B)

>(subseq ’(a b c d) 1) (B C D)

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

Функция­ reverse возвра­ща­ет­ после­до­ва­тель­ность,­ содер­жа­щую­ исход­­ ные элемен­ты­ в обрат­ном­ поряд­ке:­

> (reverse ’(a b c)) (C B A)

С помо­щью­ reverse можно,­ напри­мер,­ искать­ палин­дро­мы­, т. е. после­до­­ ватель­но­сти,­ читае­мые­ одина­ко­во­ в прямом­ и обрат­ном­ поряд­ке­ (на­ пример,­ (a b b a)). Две поло­ви­ны­ палин­дро­ма­ с четным­ коли­че­ст­вом­ аргу­мен­тов­ будут­ зеркаль­ны­ми­ отра­же­ния­ми­ друг друга­. Исполь­зуя­ length, subseq и reverse, опре­де­лим­ функцию­ mirror?1:

(defun mirror? (s)

(let ((len (length s))) (and (evenp len)

(let ((mid (/ len 2))) (equal (subseq s 0 mid)

(reverse (subseq s mid)))))))

Эта функция­ опре­де­ля­ет,­ явля­ет­ся­ ли список­ таким­ палин­дро­мом:­

> (mirror? ’(a b b a)) T

Для сорти­ров­ки­ после­до­ва­тель­но­стей­ в Common Lisp есть встроен­ная­ функция­ sort. Она прини­ма­ет­ список,­ подле­жа­щий­ сорти­ров­ке,­ и функ­ цию сравне­ния­ от двух аргу­мен­тов:­

> (sort ’(0 2 1 3 8) #’>) (8 3 2 1 0)

С функци­ей­ sort следу­ет­ быть осто­рож­ны­ми,­ пото­му­ что она дест­рук­­ тивна­. Из сооб­ра­же­ний­ произ­во­ди­тель­но­сти­ sort не созда­ет­ новый­ спи­ сок, а моди­фи­ци­ру­ет­ исход­ный­. Поэто­му­ если­ вы не хоти­те­ изме­нять­ исход­ную­ после­до­ва­тель­ность,­ пере­дай­те­ в функцию­ ее копию­.°

Исполь­зуя­ sort и nth, запи­шем­ функцию,­ кото­рая­ прини­ма­ет­ целое­ чис­ ло n и возвра­ща­ет­ n-й элемент­ в поряд­ке­ убы­вания:­

1Ричард­ Фейтман­ указал,­ что есть более­ простой­ способ­ провер­ки­ палин­дро­­ ма, а именно:­ (equal x (reverse x)). – Прим. перев­.

3.12. Стопка

63

(defun nthmost (n lst)

 

(nth (- n 1)

(sort (copy-list lst) #’>)))

Мы выну­ж­де­ны­ вычи­тать­ едини­цу,­ пото­му­ что nth индек­си­ру­ет­ элемен­­ ты, начи­ная­ с нуля,­ а наша­ nthmost – с едини­цы­.

> (nthmost 2 ’(0 2 1 3 8)) 3

Наша­ реали­за­ция­ nthmost не самая­ эффек­тив­ная,­ но мы пока­ не будем­ вдавать­ся­ в тонко­сти­ ее опти­ми­за­ции­.

Функции­ every и some приме­ня­ют­ преди­кат­ к одной­ или несколь­ким­ по­ следо­ва­тель­но­стям­. Если­ пере­да­на­ только­ одна­ после­до­ва­тель­ность,­ они прове­ря­ют,­ удовле­тво­ря­ет­ ли каждый­ ее элемент­ этому­ преди­ка­ту:­

>(every #’oddp ’(1 3 5))

T

>(some #’evenp ’(1 2 3))

T

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

> (every #’> ’(1 3 5) ’(0 2 4)) T

Если­ после­до­ва­тель­но­сти­ имеют­ разную­ длину,­ кратчай­шая­ из них огра­­ ничи­ва­ет­ диапа­зон­ провер­ки­.

3.12. Стопка

Представ­ле­ние­ списков­ в виде­ ячеек­ позво­ля­ет­ легко­ исполь­зо­вать­ их в каче­ст­ве­ стопки­ (stack). В Common Lisp есть два макро­са­ для рабо­ты­ со списком­ как со стопкой:­ (push x y) кладет­ объект­ x на верши­ну­ стоп­ ки y, (pop x) снима­ет­ со стопки­ верхний­ элемент­. Оба эти макро­са­ можно­ опре­де­лить­ с помо­щью­ функции­ setf. Вызов­

(push obj lst)

трансли­ру­ет­ся­ в

(setf lst (cons obj lst))

а вызов­

(pop lst)

в

(let ((x (car lst)) (setf lst (cdr lst)) x)

64

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

Так, напри­мер­

>(setf x ’(b))

(B)

>(push ’a x) (A B)

>x

(A B)

>(setf y x) (A B)

>(pop x)

A

>x

(B)

>y (A B)

Структу­ра­ ячеек­ после­ выпол­не­ния­ приве­ден­ных­ выра­же­ний­ пока­за­на­ на рис. 3.9.

x =

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

y =

 

 

 

 

 

 

 

 

 

 

nil

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

a

 

 

 

 

b

 

 

 

 

 

 

 

 

 

 

 

 

 

Рис. 3.9. Эф­фект от push и pop

С помо­щью­ push мож­но также­ опре­де­лить­ итера­тив­ный­ вари­ант­ функ­ ции reverse для списков:­

(defun our-reverse (lst) (let ((acc nil))

(dolist (elt lst) (push elt acc))

acc))

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

Макрос­ pushnew похож­ на push, одна­ко­ исполь­зу­ет­ adjoin вместо­ cons:

> (let ((x ’(a b))) (pushnew ’c x) (pushnew ’a x) x)

(C A B)

Элемент­ a уже присут­ст­ву­ет­ в списке,­ поэто­му­ не добав­ля­ет­ся­.

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