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

6

Функции

Пони­ма­ние­ функций ­– один из ключей­ к пони­ма­нию­ всего­ Лиспа­. Функ­ ции – основ­ная­ концеп­ция,­ кото­рая­ легла­ в осно­ву­ этого­ языка­. На прак­ тике­ они явля­ют­ся­ наибо­лее­ полез­ным­ инст­ру­мен­том,­ нахо­дя­щим­ся­ в вашем­ распо­ря­же­нии­.

6.1. Глобальные функции

Преди­кат­ fboundp сооб­ща­ет,­ суще­ст­ву­ет­ ли функция­ с именем,­ задан­­ ным в виде­ симво­ла­. Если­ какой­-либо­ символ­ явля­ет­ся­ именем­ функ­ ции, то ее мож­но полу­чить­ с помо­щью­ symbol-function:

>(fboundp ’+)

T

>(symbol-function ’+) #<Compiled-Function + 17BA4E>

Исполь­зуя­ setf над symbol-function:

> (setf (symbol-function ’add2) #’(lambda (x) (+ x 2)))

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

> (add2 1) 3

Факти­че­ски­ defun дела­ет­ немно­гим­ более­ чем просто­ преоб­ра­зо­ва­ние­ выра­же­ния­ типа­

(defun add2 (x) (+ x 2))

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

112

Глава 6. Функции

форма­цию­ о функции,­ хотя,­ строго­ гово­ря,­ исполь­зо­вать­ defun при на­ писа­нии­ программ­ вовсе­ не обяза­тель­но­.

Сделав­ первым­ аргу­мен­том­ defun выра­же­ние­ вида­ (setf f), вы опре­де­ли­­ те, что будет­ проис­хо­дить,­ когда­ setf вызван­ с первым­ аргу­мен­том­ f.° Ниже­ приве­ден­ при­мер функции­ primo, кото­рая­ явля­ет­ся­ анало­гом­ car:

(defun primo (lst) (car lst))

(defun (setf primo) (val lst) (setf (car lst) val))

В послед­нем­ опре­де­ле­нии­ на месте­ имени­ функции­ нахо­дит­ся­ выра­же­­ ние (setf primo), первым­ пара­мет­ром­ явля­ет­ся­ уста­нав­ли­вае­мое­ значе­­ ние, а вторым ­– аргу­мент­ primo.°

Теперь­ любой­ setf для primo будет­ являть­ся­ вызо­вом­ функции,­ опре­де­­ лен­ной вы­ше:

> (let ((x (list ’a ’b ’c))) (setf (primo x) 480)

x) (480 B C)

Совсем­ не обяза­тель­но­ опре­де­лять­ саму­ функцию­ primo, чтобы­ опре­де­­ лить пове­де­ние­ (setf primo), одна­ко­ такие­ опре­де­ле­ния­ обычно­ дают­ся­ пара­ми­.

Посколь­ку­ строки­ в Лиспе ­– полно­цен­ные­ выра­же­ния,­ ничто­ не меша­­ ет им нахо­дить­ся­ внутри­ тела­ функции­. Сама­ по себе­ строка­ не вызы­­ва­ ет побоч­ных­ эффек­тов,­ поэто­му­ она ни на что не влияет,­ если­ не явля­ет­­ ся послед­ним­ выра­же­ни­ем­. Если­ же строка­ будет­ первым­ выра­же­ни­ем,­ она будет­ считать­ся­ строкой­ доку­мен­та­ции­ к функции:­

(defun foo (x)

"Implements an enhanced paradigm of diversity." x)

Доку­мен­та­ция­ к функции,­ опре­де­лен­­ной глобаль­но,­ может­ быть полу­­ чена­ с помо­щью­ documentation:

> (documentation ’foo ’function)

"Implements an enhanced paradigm of diversity."

6.2. Локальные функции

Функции,­ опре­де­лен­­ные с помо­щью­ defun или setf вместе­ с symbol-func­ tion, явля­ют­ся­ глобаль­ны­ми­. Как и глобаль­ные­ пере­мен­ные,­ они могут­ быть исполь­зо­ва­ны­ везде­. Кроме­ того,­ есть возмож­ность­ опре­де­лять­ ло­ кальные­ функции,­ кото­рые,­ как и локаль­ные­ пере­мен­ные,­ доступ­ны­ лишь внутри­ опре­де­лен­­ного­ контек­ста­.

Локаль­ные­ функции­ могут­ быть опре­де­ле­ны­ с помо­щью­ конст­рук­ции­ labels – своего­ рода­ let для функций­. Ее первым­ аргу­мен­том­ явля­ет­ся­ не

6.3. Списки параметров

113

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

(name parameters . body)

Внутри­ labels любое­ выра­же­ние­ с name экви­ва­лент­но­ лямбда­-вызо­ву­

(lambda­ parameters . body).

> (labels ((add10 (x) + x 10)) (consa (x) (cons ’a x)))

(consa (add10 3))) (A . 13)

Анало­гия­ с let, одна­ко,­ не рабо­та­ет­ в одном­ случае­. Локаль­ная­ функ­ ция в labels может­ ссылать­ся­ на любые­ другие­ функции,­ опре­де­лен­­ные в той же конст­рук­ции­ labels, в том числе­ и на саму­ себя­. Это позво­ля­ет­ созда­вать­ с помо­щью­ labels локаль­ные­ рекур­сив­ные­ функции:­

> (labels ((len (lst)

(if (null lst) 0

(+ (len (cdr lst)) 1)))) (len ’(a b c)))

3

В разделе 5.2 гово­ри­лось­ о том, что конст­рук­ция­ let может­ рассмат­ри­­ ваться­ как вызов­ функции­. Анало­гич­но­ выра­же­ние­ с do может­ считать­­ ся вызо­вом­ рекур­сив­ной­ функции­. Выра­же­ние:­

(do ((x a (b x)) (y c (d y)))

((test x y) (z x y)) (f x y))

экви­ва­лент­но­

(labels ((rec (x y)

(cond ((test x y) (z x y)) (t

(f x y)

(rec (b x) (d y))))))

(rec a c))

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

6.3.Списки параметров

Вразделе 2.1 было­ пока­за­но,­ что благо­да­ря­ префикс­ной­ нота­ции­ + мо­ жет при­нимать­ любое­ коли­че­ст­во­ аргу­мен­тов­. С тех пор мы позна­ко­ми­­ лись с многи­ми­ подоб­ны­ми­ функция­ми­. Чтобы­ напи­сать­ такую­ функ­ цию само­стоя­тель­но,­ нам придет­ся­ восполь­зо­вать­ся­ оста­точ­ным­ па­ рамет­ром­ (rest).

114

Глава 6. Функции

Если мы помес­тим­ элемент­ &rest перед­ послед­ней­ пере­мен­ной­ в списке­ пара­мет­ров­ функции,­ то при вызо­ве­ этой функции­ послед­ний­ пара­метр­ полу­чит­ список­ всех остав­ших­ся­ аргу­мен­тов­. Теперь­ мы можем­ напи­­ сать funcall с помо­щью­ apply:

(defun our-funcall (fn &rest args) (apply fn args))

Также­ нам уже знако­мы­ пара­мет­ры,­ кото­рые­ могут­ быть пропу­ще­ны­ и в таком­ случае­ полу­чат­ значе­ние­ по умолча­нию­. Такие­ пара­мет­ры­ на­ зы­вают­ся­ необя­за­тель­ны­ми­ (optional). (Обычные­ пара­мет­ры­ иногда­ на­ зы­вают­ся­ обяза­тель­ны­ми­ (required).) Если­ символ­ &optional встреча­ет­­ ся в списке­ пара­мет­ров­ функции­

(defun philosoph (thing &optional property) (list thing ’is property))

то все после­дую­щие­ аргу­мен­ты­ будут­ необя­за­тель­ны­ми,­ а их значе­ни­ем­ по умолча­нию­ будет­ nil:

> (philosoph ’death) (DEATH IS NIL)

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

(defun philosoph (thing &optional (property ’fun)) (list thing ’is property))

имеет­ более­ «радо­ст­ное»­ значе­ние­ по умолча­нию:­

> (philosoph ’death) (DEATH IS FUN)

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

Пара­мет­ры­ по ключу­ (keyword) предос­тав­ля­ют­ большую­ гибкость,­ чем необя­за­тель­ные­ пара­мет­ры­. Помес­тив­ символ­ &key в список­ пара­мет­ров,­ вы поме­чае­те­ все после­дую­щие­ пара­мет­ры­ как необя­за­тель­ные­. Кроме­ того,­ при вызо­ве­ функции­ эти пара­мет­ры­ будут­ пере­да­вать­ся­ не в соот­­ ветст­вии­ с их ожидае­мым­ поло­же­ни­ем,­ а по соот­вет­ст­вую­щей­ метке ­– ключе­во­му­ слову:­

> (defun keylist (a &key x y z) (list a x y z))

KEYLIST

>(keylist 1 :y 2) (1 NIL 2 NIL)

>(keylist 1 :y 3 :x 2) (1 2 3 NIL)

Для пара­мет­ров­ по ключу, так же как и для необя­за­тель­ных,­ значе­ни­­ ем по умолча­нию­ явля­ет­ся­ nil, но в то же время­ можно­ задать­ выра­же­­ ние для их вычис­ле­ния­.

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