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

182

Глава 10. Макросы

(let ((val (calculate-something)))

(if val

 

(1+

val)

0))

 

мы можем­ напи­сать:­

(aif (calculate-something) (1+ it)

0)

Разум­ное­ исполь­зо­ва­ние­ захва­та­ пере­мен­ных­ в макро­сах­ может­ оказать­ полез­ную­ услу­гу­. Сам Common Lisp прибе­га­ет­ к такой­ возмож­но­сти­ в не­ кото­рых­ случа­ях,­ напри­мер­ в next-method-p и call-next-method­.

Разо­бран­ные­ в этом разде­ле­ макро­сы­ демон­ст­ри­ру­ют­ смысл фра­зы «про­ граммы,­ кото­рые­ пишут­ програм­мы»­. Едино­жды­ опре­де­лен­ный­ for по­ зволя­ет­ избе­жать­ напи­са­ния­ множе­ст­ва­ одно­тип­ных­ выра­же­ний­ с do. Есть ли смысл в напи­са­нии­ макро­са­ просто­ для эконо­мии­ на вводе?­ Од­ нознач­но­. Ровно­ по этой же причи­не­ мы пользу­ем­ся­ языка­ми­ програм­­ миро­ва­ния,­ застав­­ляя компи­ля­то­ры­ само­стоя­тель­но­ гене­ри­ро­вать­ ма­ шинный­ код, вместо­ того­ чтобы­ на­бирать­ его вручную­. Макро­сы­ по­ зволя­ют­ вам дать своим­ програм­мам­ те же преиму­ще­ст­ва,­ кото­рые­ вы­ соко­уров­не­вые­ языки­ дают­ програм­ми­ро­ва­нию­ в целом­. Акку­рат­ное­ приме­не­ние­ макро­сов­ позво­лит­ суще­ст­вен­но­ сокра­тить­ размер­ вашей­ програм­мы,­ облег­чая­ тем самым­ ее напи­са­ние,­ пони­ма­ние­ и поддерж­ку­.

Если­ у вас все еще оста­лись­ какие­-либо­ сомне­ния,­ представь­те­ ситуа­­ цию, когда­ встроен­ные­ макро­сы­ недос­туп­ны­ и код, в кото­рый­ они рас­ крыва­лись­ бы, необ­хо­ди­мо­ наби­рать­ вручную­. Теперь­ подой­ди­те­ к во­ просу­ с другой­ сторо­ны­. Представь­те,­ что вы пише­те­ програм­му,­ содер­­ жащую­ множе­ст­во­ сходных­ частей­. Задай­те­ себе­ вопрос:­ «А не пишу­ ли я резуль­та­ты­ раскры­тия­ макро­сов?»­ Если­ так, то макро­сы,­ гене­ри­рую­­ щие этот код, – вот то, что вам нуж­но писать­ в дейст­ви­тель­но­сти­.

10.8. На Лиспе

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

Джон Фоде­ра­ро­ назвал­ Лисп «програм­ми­руе­мым­ языком­ програм­ми­­ рова­ния»­.° С помо­щью­ функций­ и макро­сов­ Лисп может­ быть превра­­ щен в практи­че­ски­ любой­ другой­ язык. (Подоб­ный­ пример­ вы найде­те­ в главе 17.) Каким­ бы ни был наилуч­ший­ путь напи­са­ния­ програм­мы,­ може­те­ быть увере­ны,­ что сможе­те­ подог­нать­ Лисп под него­.

Макро­сы­ явля­ют­ся­ одним­ из ключе­вых­ ингре­ди­ен­тов­ гибко­сти­ Лиспа­. Они позво­ля­ют­ не просто­ изме­нять­ язык до неуз­на­вае­мо­сти,­ но и делать­

Итоги главы

183

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

Итоги главы

1.Вызов­ eval – один из спосо­бов­ исполь­зо­вать­ списки­ как код, но он не­ эффек­ти­вен­ и не нужен­.

2.Чтобы­ опре­де­лить­ макрос,­ вам необ­хо­ди­мо­ описать­ то, во что он бу­ дет раскры­вать­ся­. На самом­ деле,­ макро­сы ­– это функции,­ возвра­­ щающие­ выра­же­ния­.

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

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

5.Пробле­ма­ повтор­ных­ вычис­ле­ний­ возни­ка­ет­ в большин­ст­ве­ макро­­ сов, раскры­ваю­щих­ся­ в вызо­вы­ setf.

6.Макро­сы­ более­ гибки­ по сравне­нию­ с функция­ми­ и позво­ля­ют­ соз­ давать­ более­ широ­кий­ круг утилит­. Вы даже­ може­те­ извле­кать­ поль­ зу из возмож­но­сти­ захва­та­ пере­мен­ных­.

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

Упражнения

1.Пусть x = a, y = b, z = (c d). Запи­ши­те­ выра­же­ния­ с обрат­ной­ кавыч­­ кой, содер­жа­щие­ только­ задан­ные­ пере­мен­ные­ (x, y и z) и приво­дя­­ щие к следую­щим­ резуль­та­там:­

(a)((C D) A Z)

(b)(X B C D)

(c)((C D A) Z)

2.Опре­де­ли­те­ if через­ cond.

3.Опре­де­ли­те­ макрос,­ аргу­мен­та­ми­ кото­ро­го­ явля­ют­ся­ число­ n и сле­ дующие­ за ним произ­воль­ные­ выра­же­ния­. Макрос­ должен­ возвра­­ щать значе­ние­ n-го выра­же­ния:­

> (let ((n 2))

(nth-expr n (/ 1 0) (+ 1 2) (/ 1 0)))

3

184

Глава 10. Макросы

4.Опре­де­ли­те­ ntimes (стр. 175), раскры­ваю­щий­ся­ в (локаль­ную)­ рекур­­ сивную функцию­ вместо­ вызо­ва­ do.

5.Опре­де­ли­те­ макрос­ n-of, прини­маю­щий­ на вход число­ n и выра­же­­ ние и возвра­щаю­щий­ список­ значе­ний,­ полу­чен­­ных после­до­ва­тель­­ ным вычис­ле­ни­ем­ этого­ выра­же­ния­ для возрас­таю­ще­го­ n раз значе­­ ния пере­мен­ной:­

> (let ((i 0) (n 4)) (n-of n (incf i)))

(1 2 3 4)

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

7.Что не так со следую­щим­ опре­де­ле­ни­ем­ push?

(defmacro push (obj lst)

‘(setf ,lst (cons ,obj ,lst)))

Приве­ди­те­ пример­ ситуа­ции,­ в кото­рой­ данный­ код будет­ рабо­тать­ не так, как настоя­щий­ push.

8. Опре­де­ли­те­ макрос,­ удваи­ваю­щий­ свой аргу­мент:­

> (let ((x 1)) (double x) x)

2

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