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

268 Глава 16. Пример: генерация HTML

> (with-link ’capture

(princ "The Captured Variable"))

<a href="capture.html">The Captured Variable</a> "</a>"

Он исполь­зу­ет­ся­ в функции­ link-item, кото­рая­ прини­ма­ет­ строку­ и соз­ дает­ элемент­ списка,­ также­ являю­щий­ся­ ссылкой,­

>(link-item ’bq "Backquote!") <li><a href="bq.html"> Backquote!</a> "</a>"

ив функции­ button, кото­рая­ гене­ри­ру­ет­ ссылку­ внутри­ квадрат­ных­ скобок,­

>(button ’help "Help")

[ <a href="help.html">Help</a> ] NIL

так что она напо­ми­на­ет­ кнопку­.

16.3.Утилита для итерации

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

Наша­ новая­ утили­та­ (рис. 16.6) будет­ разно­вид­но­стью­ mapc. Она прини­­ мает­ функцию­ трех аргу­мен­тов­ и список­. Для каждо­го­ элемен­та­ списка­ функция­ приме­ня­ет­ся­ как для само­го­ элемен­та,­ так и для предше­ст­­ вующе­го­ и после­дую­ще­го­ элемен­тов­. (Если­ предше­ст­вую­ще­го­ или по­ следую­ще­го­ элемен­та­ нет, то вместо­ них исполь­зу­ет­ся­ nil.)

> (map3 #’(lambda (&rest args) (princ args)) ’(a b c d))

(A NIL B) (B A C) (C B D) (D C NIL) NIL

Как и mapc, она всегда­ возвра­ща­ет­ nil.1 Ситуа­ции,­ в кото­рых­ требу­ет­ся­ подоб­ная­ утили­та,­ возни­ка­ют­ доволь­но­ часто­. С одной­ из них мы столк­

1На самом­ деле,­ mapc возвра­ща­ет­ значе­ние­ своего­ второ­го­ аргу­мен­та­. – Прим. перев­.

16.4. Генерация страниц

269

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

(defun map3 (fn lst)

(labels ((rec (curr prev next left) (funcall fn curr prev next) (when left

(rec (car left) curr

(cadr left) (cdr left)))))

(when lst

(rec (car lst) nil (cadr lst) (cdr lst)))))

Рис. 16.6. Итера­ция­ по тройкам­

Вари­ан­том­ общей­ пробле­мы­ может­ быть случай,­ когда­ вам нужно­ сде­ лать что-либо­ между­ каждой­ парой­ элемен­тов­ списка:­

> (map3 #’(lambda (c p n) (princ c)

(if n (princ " | "))) ’(a b c d))

A | B | C | D NIL

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

16.4. Генерация страниц

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

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

270

Глава 16. Пример: генерация HTML

осуще­ст­в­ля­ет­ся­ пере­ход­ от пункта­ к разде­лу,­ а от разде­ла­ к оглав­ле­­ нию. Кроме­ того,­ должен­ быть индекс ­– отдель­ная­ страни­ца­ со ссылка­­ ми, на кото­рой­ пере­чис­ля­ют­ся­ все пункты­ в алфа­вит­ном­ поряд­ке­. Опи­ санная­ иерар­хия­ изобра­же­на­ на рис. 16.7.

section item

contents

section

item

index

section item

Рис. 16.7. Структу­ра­ сайта­

Опера­то­ры­ и структу­ры­ данных,­ необ­хо­ди­мые­ для созда­ния­ страниц,­ представ­ле­ны­ на рис. 16.8. Наша­ програм­ма­ будет­ рабо­тать­ с двумя­ ти­ пами­ объек­тов:­ пункта­ми,­ содер­жа­щи­ми­ блоки­ текста,­ и разде­ла­ми,­ содер­жа­щи­ми­ списки­ ссылок­ на пункты­.

(defparameter *sections* nil)

(defstruct item id title text)

(defstruct section id title items)

(defmacro defitem (id title text)

‘(setf ,id

 

(make-item :id

’,id

:title

,title

:text

,text)))

(defmacro defsection (id title &rest items)

‘(setf ,id

 

(make-section :id

’,id

:title ,title

:items (list ,@items))))

(defun defsite (&rest sections) (setf *sections* sections))

Рис. 16.8. Созда­ние­ сайта­

И разде­лы,­ и пункты­ содер­жат­ поле­ id. В каче­ст­ве­ содер­жи­мо­го­ этого­ поля­ будут­ пере­да­вать­ся­ симво­лы,­ имеющие­ два приме­не­ния­. Первое­ заклю­ча­ет­ся­ в опре­де­ле­ни­ях­ defitem и defsection; здесь id – это уникаль­­ ное имя, по ко­торо­му­ мы будем­ ссылать­ся­ на пункт или раздел­. Второе­

16.4. Генерация страниц

271

приме­не­ние:­ мы будем­ исполь­зо­вать­ id в имени­ файла,­ представ­ляю­ще­­ го пункт или раздел­. К приме­ру,­ страни­ца,­ представ­ляю­щая­ пункт foo, будет­ запи­са­на­ в файл "foo.html".

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

Поря­док­ следо­ва­ния­ пунктов­ в разде­ле­ опре­де­ля­ет­ся­ из аргу­мен­тов­ defsection­, поря­док­ следо­ва­ния­ разде­лов­ в оглав­ле­нии ­– из аргу­мен­тов­ defsite­.

На рис. 16.9. представ­ле­ны­ функции,­ гене­ри­рую­щие­ оглав­ле­ние­ и ин­ декс. Констан­ты­ contents и index явля­ют­ся­ строка­ми,­ служа­щи­ми­ заго­­ ловка­ми­ этих страниц­ и имена­ми­ соот­вет­ст­вую­щих­ файлов­.

(defconstant

contents

"contents")

(defconstant

index

"index")

(defun gen-contents (&optional (sections *sections*)) (page contents contents

(with ol

(dolist (s sections)

(link-item (section-id s) (section-title s)) (brs 2))

(link-item index (string-capitalize index)))))

(defun gen-index (&optional (sections *sections*)) (page index index

(with ol

(dolist (i (all-items sections)) (link-item (item-id i) (item-title i)) (brs 2)))))

(defun all-items (sections) (let ((is nil))

(dolist (s sections)

(dolist (i (section-items s))

(setf is (merge ’list (list i) is #’title<))))

is))

(defun title< (x y)

(string-lessp (item-title x) (item-title y)))

Рис. 16.9. Гене­ра­ция­ индек­са­ и оглав­ле­ния­

Функции­ gen-contents и gen-index в целом­ похо­жи­ друг на друга­. Каж­ дая из них откры­ва­ет­ HTML-файл, гене­ри­ру­ет­ заго­ло­вок­ и список­ ссы­ лок. Разни­ца­ лишь в том, что список­ пунктов­ в индек­се­ должен­ быть отсор­ти­ро­ван­. Он строит­ся­ с помо­щью­ функции­ all-items, кото­рая­ ис­ пользу­ет­ функцию­ упоря­до­че­ния­ title<. Важно,­ что все заго­лов­ки­ срав­

272

Глава 16. Пример: генерация HTML

нива­ют­ся­ функци­ей­ string-lessp, кото­рая,­ в отли­чие­ от string<, игно­ри­­ рует­ регистр­ знаков­.

В реаль­ном­ прило­же­нии­ сравне­ние­ может­ быть более­ утончен­­ным, на­ пример,­ оно может­ игно­ри­ро­вать­ началь­ные­ артик­ли­ «a» и «the».

Остав­ший­ся­ код пока­зан­ на рис. 16.10: gen-site гене­ри­ру­ет­ весь набор­ веб-страниц,­ осталь­ные­ функции­ гене­ри­ру­ют­ разде­лы­ и пункты­.

(defun gen-site ()

(map3 #’gen-section *sections*) (gen-contents)

(gen-index))

(defun gen-section (sect <sect sect>)

(page (section-id sect) (section-title sect) (with ol

(map3 #’(lambda (item <item item>) (link-item (item-id item)

(item-title item))

(brs 2)

(gen-item sect item <item item>)) (section-items sect)))

(brs 3)

(gen-move-buttons (if <sect (section-id <sect)) contents

(if sect> (section-id sect>)))))

(defun gen-item (sect item <item item>) (page (item-id item) (item-title item)

(princ (item-text item)) (brs 3)

(gen-move-buttons (if <item (item-id <item)) (section-id sect)

(if item> (item-id item>)))))

(defun gen-move-buttons (back up forward) (if back (button back "Back"))

(if up (button up "Up"))

(if forward (button forward "Forward")))

Рис. 16.10. Гене­ра­ция­ сайта,­ разде­лов­ и пунктов­

Под полным­ набо­ром­ страниц­ пони­ма­ет­ся­ оглав­ле­ние,­ индекс,­ страни­­ цы, представ­ляю­щие­ каждый­ из разде­лов,­ и страни­цы,­ представ­ляю­­ щие каждый­ из пунктов­. Оглав­ле­ние­ и индекс­ созда­ют­ся­ с помо­щью­ функций,­ представ­лен­­ных на рис. 16.9. Разде­лы­ и пункты­ гене­ри­ру­ют­­ ся gen-section, кото­рая­ созда­ет­ страни­цу­ разде­ла­ и вызы­­вает­ gen-item для гене­ра­ции­ страниц,­ представ­ляю­щих­ каждый­ пункт в данном­ раз­ деле­.

16.4. Генерация страниц

273

Эти две функции­ начи­на­ют­ся­ и закан­чи­ва­ют­ся­ похо­жим­ обра­зом:­ обе прини­ма­ют­ аргу­мен­ты,­ представ­ляю­щие­ сам объект,­ а также­ преды­ду­­ щий и после­дую­щий­ объек­ты­ того­ же уровня­ вложен­но­сти;­ обе закан­­ чива­ют­ся­ вызо­вом­ gen-move-buttons с целью­ сгене­ри­ро­вать­ кнопки­ для пере­ме­ще­ния­ вперед,­ назад­ и вверх. Разни­ца­ в сере­ди­не:­ в отли­чие­ от gen-section, ко­торая­ произ­во­дит­ упоря­до­чен­ный­ список­ ссылок­ на пунк­ ты, gen-item просто­ отправ­ля­ет­ текст в вы­ходной­ файл.

Каким­ будет­ текст каждо­го­ из пунктов ­– личное­ дело­ пользо­ва­те­ля­. Этот текст, к приме­ру,­ вполне­ может­ содер­жать­ HTML-размет­ку­. Или же он может­ быть сгене­ри­ро­ван­ другой­ програм­мой­.

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

(defitem des "Fortune Cookies: Dessert or Fraud?" "...") (defitem case "The Case for Pessimism" "...") (defsection position "Position Papers" des case) (defitem luck "Distribution of Bad Luck" "...")

(defitem haz "Health Hazards of Optimism" "...") (defsection abstract "Research Abstracts" luck haz) (defsite position abstract)

Рис. 16.11. Неболь­шой­ сайт

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