Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

книги / Функциональное программирование

..pdf
Скачиваний:
0
Добавлен:
12.11.2023
Размер:
14.84 Mб
Скачать

Рассмотрим примеры работы базовых функций:

>(car ‘(one two three)) ONE

>(cdr ‘(first))

NIL

>(cons 1 ‘(2 3)) (1 2 3)

>(equal ‘(1 2 3) ‘(1 2 3))

T

>(atom ‘one)

T

Первые две базовые функции CAR и CDR являются противоположными для функции CONS. Проводя композицию вызовов CAR и CDR, можно выделить любой элемент из списка. Например, выделим из списка ((a b c) d) элемент С.

>(cdr (cdr (car '((a b c) d))))

(c)

Вложенные вызовы CAR и CDR

Композицию вызовов CAR и CDR можно записать в виде одной функции:

(C…R список).

Вместо многоточия записывается нужная комбинация из букв A и D (для CAR и CDR соответственно). В один вызов можно объединять не более четырех функций CAR и CDR.

(CADAR x) (CAR (CDR (CAR x))) >(CDDAR ‘((a b c d) e))

(C D) >(CDDR ‘(k l m))

(M)

Функция NTH извлекает n-й элемент из списка. Форма записи

NTH < n > <список >

31

Нумерация в разных версиях «Лиспа» ведется по-разному, например в MCL с 0, в CLisp с 1.

Функция list создает список из S-выражений (списков или атомов).

Форма записи и число аргументов может быть любое. Например,

>(list 1 2) (1 2)

Функция length возвращает в качестве значения длину списка, т.е. число элементов на верхнем уровне.

Например, >(length ‘(1 2 3)) 3

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

(+ x1 x2 … xn) возвращает x1 + x2 + x3 + … + xn. (– x1 x2 … xn) возвращает x1 – x2 – x3 – … – xn. (* y1 y2 … yn) возвращает y1 * y2 * y3 * … * yn. (/ x1 x2 … xn) возвращает x1 / x2 / … / xn.

Для объединения предикатов в сложные выражения и для выделения элементов в «Лиспе» используются логические функ-

ции AND, OR, NOT.

Функция NOT берет один аргумент и возвращает противоположное логическое значение.

(NOT < s-выражение >)

Если аргумент равен nil, NOT возвращает значение t и наоборот.

Логическая функция OR (логическое ИЛИ) возвращает значение первого аргумента, который не nil.

(OR < arg-1 > < arg-2 > < arg-3 > ...)

OR возвращает значение не-NIL, если имеется хотя бы один аргумент, отличный от NIL.

32

Логическая функция AND (логическое И) работает так же, как и OR, с той лишь разницей, что здесь все аргументы должны быть отличны от NIL.

(AND < arg-1 > < arg-2 > < arg-3 >...)

1.13.Управляющие структуры

В«Лиспе» существует четыре вида функций для управления процессом вычисления: let, prog, cond и do.

Предложение let служит для одновременного присваивания некоторых значений некоторым переменным.

(let (

(var_1 value_1) (var_2 value_2)

(var_n value_n)

)

form_1 form_2 … form_m)

Переменным var_1, var_2, … var_n присваиваются

(параллельно!) значения value_1, value_2, … value_n, а затем последовательно вычисляются значения форм form_1 form_2

form_m. В качестве значения всего функции let возвращается значение последней вычисленной формы, т.е. form_m.

>(let ((x 3) (y 4)) (sqrt (+ (* x x) (* y y))))

5.0

Предложение let выполняет локальные присваивания, т.е. после выполнения переменные var_1, var_2, … var_n получают начальные значения, которые они имели до исполнения.

Управляющая конструкция prog имеет три разновидности: prog1, prog2, prong. Общий вид записи конструкции prog следующий:

(prog* form_1 form_2 … form_n).

33

В этой конструкции последовательно вычисляются значения форм form_1, form_2, …, form_n, в качестве результата для предложения prog1 будет назначено значение, которое имеет первая форма (form_1), для prog2 – значение второй формы (form_2),

адля progn – значение последней формы (form_n). Например,

>(prog1 (setq x 2) (setq x (* x 2)) (setq x (* x 2)) (setq x (* x 2)))

2

>(prog2 (setq x 2) (setq x (* x 2)) (setq x (* x 2)) (setq x (* x 2)))

4

>(progn (setq x 2) (setq x (* x 2)) (setq x (* x 2)) (setq x (* x 2))) 16

Функция cond служит для организации разветвляющихся

действий, т.е. функция множественного ветвления: (cond

(predicate1 form1)

(predicate2 form21 form22 … form2M) (predicate3)

(predicateN formN)

)

Функция cond последовательно вычисляет значения пре-

дикатов predicate1, predicate2, predicate3, …, predicateN, и значе-

ние формы первого предиката, равного true, возвращается в качестве значения всей функции cond.

Если у предиката нет вычислимой формы, как, например, predicate3, то значением функции cond будет назначено значение предиката.

Если все предикаты имеют значение nil, то для исключения аварийного выхода из функции используют константу истинности T, т.е. просто дописывают в качестве последнего предиката специальный символ Т.

Рекурсия – механизм старый, но требует определенного склада ума, по этой причине во многих функциональных языках используют циклическую функцию, или предложение DO, аналог оператора цикла с параметрами for.

34

(do

((var_1 value_1) (var_2 value_2) … (var_n value_n)) (condition form_yes_1 form_yes_2 … form_yes_m) form_no_1 form_no_2 … form_no_k

)

Механизм работы функции DO прост: переменным var_1, var_2, …, var_n параллельно присваиваются значения value_1, value_2, …, value_n; затем при выполнении условия condition

последовательно выполняются формы form_yes_1, form_yes_2, …, form_yes_m, и значение последней формы form_yes_m возвращается в качестве значения функции DO; если условие condition не выполнено, то последовательно выполняются формы form_no_1, form_no_2, …, form_no_k, и возвращается в качестве значения функции DO значение последней формы form_no_k.

Другие условные предложения

Условное предложение IF работает в точности как условная конструкция в любом языке программирования.

(IF < условие > < то форма > < иначе форма >)

Условные предложения WHEN и UNLESS являются частными случаями условного предложения IF.

Если условие соблюдается, то выполняются формы: (WHEN < условие >

< форма-1 > < форма-2 > < форма-3 > …).

Если условие не соблюдается, то выполняются формы: (UNLESS < условие >

< форма-1 > < форма-2 > < форма-3 > …).

1.14. Функции ввода-вывода

Функция READ отличается от операторов ввода-вывода в других языках программирования тем, что она обрабатывает вводимое выражение целиком, а не по одному.

Вызов функции осуществляется в виде (READ).

35

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

Например, (defun tr (arg)

(list (+ arg (read)) (read))) >(tr 8)

14 cat

(22 cat)

Функция PRINT – это функция с одним аргументом. Она выводит значение аргумента на монитор, а затем возвращает значение аргумента.

Например, (PRINT < arg >) > (print (+ 2 3)) 5 5

Можно задать логичный вопрос: «Почему в выводе два значения?». Нет, это не опечатка и не ошибка. Объяснение очень простое: у всякой функции должно быть значение, иначе это уже не функция. Первое значение в выводе – это значение самой функции PRINT, т.е. значение ее аргумента. «Лисп» показывает его пользователю. Второе выведенное значение – это побочный эффект, а именно печать этого значения.

PRINT и READ относятся к классу псевдофункций, о которых будет рассказано позже. Псевдофункция – это функция, у которой кроме своего значения есть побочный эффект. У функции PRINT это вывод на экран.

36

1.15. Процесс передачи параметров

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

Формальные параметры функции действуют только в пределах этой функции, а после завершения вычислений они теряют свою силу. Согласно принципам программирования функций для любого языка программирования формальные параметры принимают значение фактических в момент вызова функции, обратный принцип не действует. Иными словами, формальные параметры функции являются строго локальными объектами и имеют значение только внутри самой функции.

Например,

>(defun f (x) (setq x ' new)) – меняет значение x F

>(setq x ' old) old

>x old >(f x) new

Свободные переменные и функции обработки списков Если в теле функции есть переменные, не входящие в число

ее формальных параметров, они называются свободными. Значения свободных переменных остаются в силе после ее выполнения.

Например,

>(defun f1 (y) (setq x 3)) f1

>(f1 5) 3

>x

3

37

Функция APPEND объединяет два и более списков в один. (APPEND< список1 > < список2 >)

Например, >(append ' (a b) ' (c)) (A B C)

APPEND объединяет элементы, не изменяя их. Рассмотрим несколько примеров, чтобы показать отличие

APPEND, LIST, CONS.

Например, >(list ' (a b) ' (c d)) ((A B) (C D))

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

>(cons ' (a b) ' (c d)) ((A B) C D)

Предложение CONS всегда помещает первый перед вторым аргументом, при этом по причине того, что хвост списка – это список, скобки раскрываются.

>(append ' (a b) ' (c d)) (A B C D)

Предложение APPEND создает новый список, раскрывая скобки у всех аргументов на уровень ниже и помещая их в один список

(табл.1).

 

 

Таблица 1

Дополнительные сведения об отличии функций

 

 

 

Функция

Аргументы

Действие

LIST

s s s …

(s s s…)

CONS

s (list)

(s list)

APPEND

(l) (l) (l) …

(l l l …)

Функция REVERSE изменяет порядок элементов в аргументе.

38

(REVERSE < список >)

Аргументом функции REVERSE должен быть обязательно список. Порядок следования элементов меняется только на верхнем уровне, порядок элементов на нижних уровнях списка остается неизменным.

>(reverse ' ((a b c) e)) (E (A B C))

Функция LAST берет из списка последний элемент на верхнем уровне.

(LAST < список >)

Например, >(last' (a b c))

(C)

Базовые предикаты Предикат – это функция, которая определяет, обладает

ли аргумент определенным свойством, и возвращает в качестве значения только два значения – T или NIL.

Предикат ATOM проверяет, является ли аргумент атомом. Значение будет Т, если является атомом, и NIL в обратном случае.

(ATOM < S-выражение >)

Результат предиката atom с пустым списком nil всегда true: >(atom nil)

T >(atom ( )) T

Предикат EQ сравнивает два выражения и возвращает Т, если они одинаковые, и nil в обратном случае.

(EQ < выражение 1 > < выражение 2 >) Примеры:

>(eq ' cat ' cat) T

39

EQ можно применять к числам, если они представлены одним типом.

>(eq 123 123) T

Предикат равенства «=» сравнивает числа различного типа. (= < число1 > < число2 >)

Предикат EQ сравнивает и числа, и символы, а предикат равенства «=» только числа или значения объектов, конкретизированные как числа.

Предикат истинен только в том случае, если все аргументы эквивалентны по ЕQ, т.е. это числа одного и того же типа и имеют одно и то же значение.

(EQL < аргумент1 > < аргумент2 >)

>(eql 'a 'a) T

>(eql '12 '12) T

EQUAL – самый общий предикат. Сравнивает не только символы, числа и списки: числа, символы и списки, если их изображения совпадают.

(EQUAL < аргумент1 > < аргумент2 >)

>(equal '(a b c) '(a b c)) T

>(equal nil '(( )))

NIL

здесь представлен список из пустого списка, т.е. список не пустой.

Предикат NULL проверяет, является ли аргумент пустым списком табл. 2.

(NULL < аргумент >)

40