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

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

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

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

2.19. Упражнение для самоконтроля 2 к теме 2

Задача 2.6

Построить функцию для нахождения значения факториала n!: = 1*2*3*…*(n – 1)*n. (используя простую рекурсию). Проверить решение, используя трассировку.

Задача 2.7

Возведение числа x в степень n с помощью умножения (используя рекурсию по аргументу). Проверить решение, используя трассировку.

Задача 2.8

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

Задача 2.9

Построить функцию для копирования списка в глубину. Воспользоваться параллельной рекурсией.

Задача 2.10

Пример взаимной рекурсии – реверс списка (обращение списка с учетом вложенных списков). Так как рекурсия взаимная, в задаче определены две функции: reverse и rearrange. Функция rearrange рекурсивна сама по себе.

Задача 2.11

Пример рекурсии более высокого порядка – второго. Классический пример функции с рекурсией второго по-

рядка – функция Аккермана.

121

Функция Аккермана определяется следующим образом: B (0, n) = n + 1

B (m, 0) = B (m – 1, 0)

B (m, n) = B (m – 1, B (m, n – 1)), где m 0 и n 0.

Задача 2.12

Определите применяющий функционал FUNCALL через применяющий функционал APPLY.

Задача 2.13

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

Например:

Дан список: (u w r).

Результат работы макроса: (r w u).

Решение:

(defmacro REV (L &optional acc) (list 'if (null L)

(cons 'quote (list acc))

(list 'REV (cdr L) (cons (car L) acc)))) >(rev (a s (d)))

((D) S A),

где «&optional acc» – объявление необязательного формального параметра макроса REV c именем acc (это некий накопитель) и значением по умолчанию (т.е. когда этого параметра нет при вызове функции) nil.

Общая форма объявления необязательных параметров

…&optional (имя_параметра_значение_по_умолчанию) …

В качестве значения_по_умолчанию можно использовать выражение, вычисляющее это значение. В макросе не вычисляются аргументы. Следовательно, при вызове макроса в блокировании вычисленияаргументовпосредством«quote»или «‘»нетпотребности.

Задача 2.14

Определитемакрос,которыйвозвращаетвсесвоиаргументы.

122

3. РАЗРАБОТКА НЕКОТОРЫХ ПРОГРАММ ДЛЯ РЕШЕНИЯ ЗАДАЧ ФУНКЦИОНАЛЬНОГО ПРОГРАММИРОВАНИЯ

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

На моделирование процессов, перевод в язык машинных команд, отладку программы тратится очень много сил и времени программиста. А что если записать нашу модель, минуя этап написания программы в операторах языка программирования? Это не так сложно сделать при помощи декларативных языков программирования, таких как «Пролог» и «Лисп».

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

3.1. Задача о восьми ферзях

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

Решение 1

Решение 2

123

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

свозвратом.

Вшахматах ферзи атакуют в любом направлении – по горизонтали, по вертикали или по диагонали шахматной доски. Задача состоит в том, чтобы найти такое размещение восьми

ферзей на доске 8 8, чтобы никакие два ферзя не атаковали друг друга.

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

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

Для компоновки всех наших функций лучше всего ис-

пользовать предложение progn (progn form_1 form_2 … form_n),

которое возвратит значение последней формы form_n.

Чтобы последовательно перебирать все строчки на текущей колонке, будем использовать рекурсивную функцию hypot:

(defun hypot (fer cols rows allrows) (if cols

(if rows (append

(hyp fer cols (car rows) allrows) (hypot fer cols (cdr rows) allrows))

nil) (listfer)))

Для представления, что два ферзя, заданных парами p1 и p2, не бьют друг друга, можно легко определить предикат check1_pair. Этот предикат выглядит следующим образом:

(defun check1_pair (p1 p2) (cond

124

((eq (– (car p1) (cdr p1)) (– (car p2) (cdr p2))) nil) ((eq (+ (car p1) (cdr p1)) (+ (car p2) (cdr p2))) nil) ('t 't)))

Для проверки, что ферзь pair не бьет ни одного из списка pairs, можно определить рекурсивный предикат check_pair:

(defun check_pair (pairs pair) (if pairs

(if(check1_pair(carpairs)pair)(check_pair(cdrpairs)pair)nil) t))

В случае если ферзя необходимо исключить из списка результирующей расстановки, используем функцию exclude:

(defun exclude (l x) (if l

(let* ((car_l (car l)))

(if (eq (car l) x) (cdr l) (cons (car l) (exclude (cdr l) x))))

nil))

Теперь можно определить функцию hyp, которая попытается разместить очередного ферзя на доске. Если данная функция успешна, то необходимо перейти к следующей колонке.

(defun hyp (fer cols row rows) (if cols

(if (check_pair fer (fr cols row)) (let* ((nxtrws (exclude rows row)))

(hypot (cons (fr cols row) fer) (cdr cols) nxtrws nxtrws)) nil)

(listfer)))

Теперь осталось самое простое – функция для записи всех расстановок ферзей на доске:

(defun ferzi (x)

(allcdrs (ferz (lnum x))))

Полный текст программы решения задачи о восьми ферзях: (progn

(defun null (x)

(if (eq x nil) t nil))

125

; -----------------------------------------------------

(defun append (a b)

(if a (cons (car a) (append (cdr a) b)) b)) ; -----------------------------------------------------

(defun check1_pair (p1 p2) (cond

((eq (- (car p1) (cdr p1)) (– (car p2) (cdr p2))) nil) ((eq (+ (car p1) (cdr p1)) (+ (car p2) (cdr p2))) nil)

('t 't)))

; конец check1_pair

; -----------------------------------------------------

 

(defun check_pair (pairs pair)

 

(if pairs

 

(if(check1_pair(carpairs)pair)(check_pair(cdrpairs)pair)nil)

t))

; check_pair

; -----------------------------------------------------

(defun hypot (fer cols rows allrows) (if cols

(if rows (append

(hyp fer cols (car rows) allrows) (hypot fer cols (cdr rows) allrows))

nil) (list fer)))

; -----------------------------------------------------

(defun fr (cols row) (cons (car cols) row))

; -----------------------------------------------------

(defun exclude (l x) (if l

(let* ((car_l (car l)))

(if (eq (car l) x) (cdr l) (cons (car l) (exclude (cdr l) x))))

nil))

; -----------------------------------------------------

(defun hyp (fer cols row rows)

126

(if cols

(if (check_pair fer (fr cols row)) (let* ((nxtrws (exclude rows row)))

(hypot (cons (fr cols row) fer) (cdr cols) nxtrws nxtrws)) nil)

(list fer)))

; -----------------------------------------------------

(defun ferz (L) (hypot nil L L L))

; -----------------------------------------------------

(defun lnum (x) (cond

((eq x 1) (list 1))

('t (cons x (lnum (– x 1))))))

; -----------------------------------------------------

(defun cdrs (x) (if x

(cons (cdr (car x)) (cdrs (cdr x))) nil))

; -----------------------------------------------------

(defun allcdrs (x) (if x (cons

(cdrs (car x)) (allcdrs (cdr x)) ) nil))

; -----------------------------------------------------

(defun ferzi (x)

(allcdrs (ferz (lnum x))))

; -----------------------------------------------------

(ferzi 8)

); конец программы

127

3.2. Задача для расчета сопротивления электрической цепи

Во всем мире проектирование интегральных схем и плат для современной электроники происходит при помощи элек- тронно-вычислительных машин (ЭВМ).

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

Рассмотрим как пример принципы построения программы для расчета сопротивления электрической цепи. Данная программа позволит смоделировать процесс сопротивления.

Для этого воспользуемся физическими формулами расчета сопротивления.

Последовательное (serial) соединение сопротивлений в электрической цепи рассчитывается по формуле

R = R1 + R2.

Параллельное (parallel) соединение сопротивлений в электрической цепи рассчитывается по формуле

R = (R1 * R2) / (R1 + R2).

Для расчета параллельного и последовательного соединения напишем две функции:

(defun s_r (R1 R2) (+ R1 R2)),

(defun p_r (R1 R2) (/ (* R1 R2) (+ R1 R2))).

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

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

128

r1 = r2 = r3 = 10 >(s_r 10 (p_r 10 10)) 15

Если добавим еще одно сопротивление параллельно, то получим:

r1 = r2 = r3 = r4 = 10

>(p_r 10 (s_r 10 (p_r 10 10))) 6

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

Во всем мире проверку правильности проектирования любых электронных схем проводят на программах искусственного интеллекта. Для справки, 90 % всех микросхем анализируется и тестируется при помощи программ, написанных на языке Lisp.

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

3.3. Упражнение для самоконтроля 1 к теме 3

Задача 3.1

Определите функцию, вычисляющую, сколько всего атомов в списке (списочной структуре).

Например: в списке (a b (c d)) атомов 4.

Задача 3.2

Определите функцию, которая, чередуя элементы списков

(a b c …) и (d e f …), образует новый список (a d b e c f …).

129

4. ПЕРСПЕКТИВЫ РАЗВИТИЯ ТЕОРИИ И ПРАКТИКИ ФУНКЦИОНАЛЬНОГО ПРОГРАММИРОВАНИЯ

Согласно рекомендациям специалистов по обучению информатике, функциональное программирование входит в число основных подходов к изучению программирования в университетах (наряду с алгоритмическим, императивным, аппаратным, объектным и обзорно-ознакомительным). В целом средства и методы функционального программирования образуют два слоя.

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

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

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

Внешний слой открывает перспективы повышения уровня используемых конструкций на базе моделирования основных механизмов системного, низкоуровневого, оптимизационного, логического, высокопроизводительного, объектно-ориентирован- ного и других подходов к разработке программ. Такие подходы расширяют понятие «функция», варьируют правила применения и реализации функций, конкретизируют расширения и специализацию систем функционального программирования.

130