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

148

Глава 8. Символы

Пусть, напри­мер,­ имеет­ся­ програм­ма,­ разде­лен­­ная на два паке­та,­ math и disp. Если­ символ­ fft экспор­ти­ру­ет­ся­ паке­том­ math, то в паке­те­ disp он будет­ досту­пен­ как math:fft. Внутри­ паке­та­ math к нему­ можно­ обра­­ щаться­ без префик­са ­– просто­ fft.

Ниже­ приво­дит­ся­ пример­ опре­де­ле­ния­ паке­та,­ кото­рое­ можно­ помес­­ тить в нача­ло­ файла:­

(defpackage "MY-APPLICATION"

(:use "COMMON-LISP" "MY-UTILITIES") (:nicknames "APP")

(:export "WIN" "LOSE" "DRAW"))

(in-package my-application)

Для созда­ния­ ново­го­ паке­та­ исполь­зу­ет­ся­ defpackage. Пакет­ my-applica­­ tion1 исполь­зу­ет­ два других­ паке­та,­ common-lisp и my-utilities. Это озна­­ чает,­ что симво­лы,­ экспор­ти­руе­мые­ ими, доступ­ны­ в новом­ паке­те­ без исполь­зо­ва­ния­ префик­сов­. Практи­че­ски­ всегда­ в созда­вае­мых­ паке­тах­ исполь­зу­ет­ся­ common-lisp, посколь­ку­ никто­ не хочет­ исполь­зо­вать­ ква­ лифи­ци­ро­ван­ное­ имя с префик­сом­ при каждом­ обра­ще­нии­ к встроен­­ ным в Лисп опера­то­рам­ и пере­мен­ным­.

Пакет­ my-applications сам экспор­ти­ру­ет­ лишь три симво­ла:­ win, lose и draw. Посколь­ку­ в defpackage было­ также­ зада­но­ сокра­щен­ное­ имя (nickna­me)­ паке­та­ – app, то и к экспор­ти­руе­мым­ симво­лам­ можно­ будет­ обра­щать­ся­ также­ и через­ него:­ app:win.

За defpackage следу­ет­ вызов­ in-package, кото­рая­ выстав­ля­ет­ теку­щим­ паке­том­ my-application. Теперь­ все новые­ симво­лы,­ для кото­рых­ не ука­ зан пакет,­ будут­ интер­ни­ро­вать­ся­ в my-application до тех пор, пока­ inpackage не изме­нит­ теку­щий­ пакет­. После­ оконча­ния­ загруз­ки­ файла,­ содер­жа­ще­го­ вызов­ in-package, значе­ние­ теку­ще­го­ паке­та­ сбрасы­ва­ет­ся­

вто, каким­ оно было­ до нача­ла­ загруз­ки­.

8.6.Ключевые слова

Симво­лы­ паке­та­ keyword (извест­ные­ как ключе­вые­ слова­) имеют­ две осо­ бенно­сти:­ они всегда­ само­вы­чис­ляе­мы­ и вы може­те­ всегда­ ссылать­ся­ на них просто­ как на :x вместо­ keyword:x. Когда­ мы только­ начи­на­ли­ ис­ пользо­вать­аргу­мен­ты­поключу­(стр.60),вызов­(member ’(a) ’((a) (z)) test: #’equal) мог пока­зать­ся­ нам более­ есте­ст­вен­­ным, чем (member ’(a) ’((a) (z)) :test #’equal). Теперь­ мы пони­ма­ем,­ поче­му­ второе,­ слегка­ неес­те­ст­­ вен­ное напи­са­ние,­ явля­ет­ся­ коррект­ным­. Двоето­чие­ перед­ именем­ сим­ вола­ позво­ля­ет­ отне­сти­ его к ключе­вым­ словам­.

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

8.7. Символы и переменные

149

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

(defun noise (animal) (case animal

(:dog :woof) (:cat :meow) (:pig :oink)))

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

8.7. Символы и переменные

В Лиспе­ есть опре­де­лен­­ная особен­ность,­ кото­рая­ может­ смущать­ но­ вичков:­ симво­лы­ могут­ быть связа­ны­ с пере­мен­ны­ми­ двумя­ различ­ны­­ ми спосо­ба­ми­. Если­ символ­ явля­ет­ся­ именем­ специ­аль­ной­ пере­мен­ной,­ ее значе­ние­ хранит­ся­ в одном­ из полей­ структу­ры­ симво­ла­ (см. рис. 8.1). Полу­чить­ доступ­ к этому­ полю­ можно­ с помо­щью­ symbol-value. Таким­ обра­зом,­ суще­ст­ву­ет­ прямая­ связь между­ симво­лом­ и представ­ляе­мой­ им специ­аль­ной­ пере­мен­ной­.

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

8.8. Пример: генерация случайного текста

Если вам предсто­ит­ напи­сать­ програм­му,­ каким­-либо­ обра­зом­ рабо­таю­­ щую со слова­ми,­ отлич­ной­ иде­ей часто­ явля­ет­ся­ исполь­­зова­ние­ симво­­ лов вместо­ строк, пото­му­ что симво­лы­ атомар­ны­. Они могут­ сравни­вать­­ ся за одну­ итера­цию­ с помо­щью­ eql, в то время­ как строки­ сравни­ва­ют­­ ся побу­к­вен­но­ с помо­щью­ string-equal или string=. В каче­ст­ве­ приме­ра­ рассмот­рим­ гене­ра­цию­ случай­но­го­ текста­. Первая­ часть програм­мы­ бу­ дет считы­вать­ обра­зец­ текста­ (чем он больше,­ тем лучше),­ соби­рая­ ин­ форма­цию­ о связях­ между­ сосед­ни­ми­ слова­ми­. Вторая­ ее часть будет­ случай­ным­ обра­зом­ выпол­нять­ прохо­ды­ по сети,­ постро­ен­ной­ ранее­ из слов исход­но­го­ текста­. После­ прохо­ж­де­ния­ каждо­го­ слова­ програм­ма­

150

Глава 8. Символы

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

На рис. 8.2 приво­дит­ся­ первая­ часть програм­мы ­– код для сбора­ инфор­­ мации­ из исход­но­го­ текста­. На его осно­ве­ строит­ся­ хеш-табли­ца­ *words*. Ключа­ми­ в ней явля­ют­ся­ симво­лы,­ соот­вет­ст­вую­щие­ словам,­ а значе­­ ниями­ будут­ ассо­циа­тив­ные­ списки­ следую­ще­го­ типа:­

((|sin| . 1) (|wide| . 2) (|sights| . 1))

(defparameter *words* (make-hash-table :size 10000))

(defconstant maxword 100)

(defun read-text (pathname)

(with-open-file (s pathname :direction :input) (let ((buffer (make-string maxword))

(pos 0))

(do ((c (read-char s nil :eof) (read-char s nil :eof)))

((eql c :eof))

(if (or (alpha-char-p c) (char= c #\’)) (progn

(setf (aref buffer pos) c) (incf pos))

(progn

(unless (zerop pos)

(see (intern (string-downcase

(subseq buffer 0 pos))))

(setf pos 0)) (let ((p (punc c)))

(if p (see p)))))))))

(defun punc (c) (case c

(#\. ’|.|) (#\, ’|,|) (#\; ’|;|) (#\! ’|!|) (#\? ’|?|) ))

(let ((prev ‘|.|)) (defun see (symb)

(let ((pair (assoc symb (gethash prev *words*)))) (if (null pair)

(push (cons symb 1) (gethash prev *words*)) (incf (cdr pair))))

(setf prev symb)))

Рис. 8.2. Чтение­ образ­ца­ текста­

8.8. Пример: генерация случайного текста

151

Выше­ приве­де­но­ значе­ние­ ключа­ |discover| для отрыв­ка­ из «Paradise Lost» Мильто­на­1. Мы видим,­ что слово­ «discover» было­ исполь­зо­ва­но­ в поэме­ четы­ре­ раза: по одно­му­ разу­ перед­ слова­ми­ «sin» и «sights» и два­ жды­ перед­ словом­ «wide». Подоб­ная­ инфор­ма­ция­ соби­ра­ет­ся­ функци­ей­ read-text, кото­рая­ строит­ такой­ ассо­циа­тив­ный­ список­ для каждо­го­ сло­ ва в задан­ном­ тексте­. При этом файл чита­ет­ся­ побу­к­вен­­но, а слова­ соби­­ рают­ся­ в строку­ buffer. С пара­мет­ром­ maxword=100 програм­ма­ сможет­ чи­ тать слова,­ состоя­щие­ не более­ чем из ста букв. Для англий­ских­ слов этого­ более­ чем доста­точ­но­.

Если­ считан­ный­ знак явля­ет­ся­ бук­вой­ (это опре­де­ля­ет­ся­ с помо­щью­ alpha-char-p) или апост­ро­фом,­ то он запи­сы­ва­ет­ся­ в буфер­. Любой­ дру­ гой символ­ сигна­ли­зи­ру­ет­ об оконча­нии­ слова,­ после­ чего­ все нако­п­­ лен­ное слово,­ интер­ни­ро­ван­ное­ в символ,­ пере­да­ет­ся­ в функцию­ see. Неко­то­рые­ знаки­ пунктуа­ции­ также­ расце­ни­ва­ют­ся­ как слова ­– функ­ ция punc выво­дит­ словес­ный­ экви­ва­лент­ задан­но­го­ знака­ пунктуа­ции­.

Функция­ see реги­ст­ри­ру­ет­ каждое­ встречен­­ное слово­. Ей также­ необ­хо­­ димо­ иметь инфор­ма­цию­ о преды­ду­щем­ слове,­ для чего­ исполь­зу­ет­ся­ пере­мен­ная­ prev. Изна­чаль­но­ она содер­жит­ псевдо­сло­во,­ соот­вет­ст­вую­­ щее точке­. Если­ see уже вызы­­валась­ хотя­ бы один раз, то prev содер­жит­ слово,­ пере­дан­ное­ этой функции­ на преды­ду­щем­ вызо­ве­.

После­ отра­бот­ки­ read-text табли­ца­ *words* будет­ содер­жать­ вхож­де­ния­ всех слов в задан­ном­ тексте­. Их коли­че­ст­во­ можно­ подсчи­тать­ с помо­­ щью hash-table-count. Англий­ские­ тексты,­ не отли­чаю­щие­ся­ особым­ словес­ным­ разно­об­ра­зи­ем,­ редко­ содер­жат­ более­ 10 000 различ­ных­ слов.

А вот теперь­ начи­на­ет­ся­ самая­ весе­лая­ часть. На рис. 8.3 представ­лен­ код, гене­ри­рую­щий­ текст с помо­щью­ кода­ на рис. 8.2. Правит­ балом­ ре­ курсив­ная­ функция­ generate-text. Ей необ­хо­ди­мо­ сооб­щить­ желае­мое­ коли­че­ст­во­ слов в созда­вае­мом­ тексте­ и (необя­за­тель­но)­ преды­ду­щее­ сло­ во. По умолча­нию­ это точка,­ и текст начи­на­ет­ся­ с ново­го­ предло­же­ния­.

Для полу­че­ния­ каждо­го­ после­дую­ще­го­ слова­ generate-text вызы­­вает­ random-text с преды­ду­щим­ словом­. Функция­ random-text случай­ным­ об­ разом­ выби­ра­ет­ одно­ из слов, следую­щих­ после­ prev в исход­ном­ тексте­. Веро­ят­ность­ выбо­ра­ одно­го­ из пере­чис­лен­­ных слов опре­де­ля­ет­ся­ в со­ ответ­ст­вии­ с часто­той­ его появ­ле­ния­ в тексте­.°

Теперь­ самое­ время­ осуще­ст­вить­ тесто­вый­ запуск­ нашей­ програм­мы­. Одна­ко­ вы уже знако­мы­ с приме­ром­ того,­ что она может­ сделать:­ речь о той самой­ строфе­ в нача­ле­ кни­ги, для гене­ра­ции­ кото­рой­ был исполь­­ зован­ отры­­вок из «Paradise Lost» Мильто­на­.°

1Речь об эпичес­кой­ поэме­ англий­ско­го­ мысли­те­ля­ Джона­ Мильто­на­ «Поте­­ рянный­ рай», издан­ной­ в 1667 году­­. Поэма­ напи­са­на­ белым­ стихом­. – Прим. перев­.

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