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

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

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

 

 

 

Таблица 2

 

Числовые предикаты

 

 

 

 

 

Предикат

Действие

T

NIL

zerop

arg = 0

(zerop 0)

(zerop 1)

plusp

arg > 0

(plusp 1)

(plusp – 1)

minusp

arg < 0

(minusp – 1)

(minusp 1)

=

arg1 = arg2 = arg3 = …

(= 2 2 2)

(= 1 2 3)

>

arg1 > arg2 > arg3 > …

(> 3 2 1)

(> 2 3 1)

<

arg1 < arg2 < arg3 < …

(< 1 2 3)

(< 2 3 1)

1.16. Псевдофункции

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

Например, рассмотренная функция PRINT имеет собственное значение,апобочнымэффектомявляетсявыводэтогозначения.

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

Основной закон «Лиспа» гласит, что всякая функция имеет значение, псевдофункция не является исключением.

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

Например,

>(list (+ (setq а 3) 4) а) (7 3)

>a

3

В этом примере внутри вызова функции создания списка переменной а «присвоили» значение 3 и здесь же, в аргументе этой же функции, это значение использовали для выдачи результата. В императивных языках это невозможно.

41

>(list b (setq b 3)) Error: Unbound atom В

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

1.17. Работа с контекстом

Для начала необходимо определить, что такое контекстное, или вычислительное, окружение. Контекст – это любое обобщение действующих связей между переменными и их значениями. Статическая связь переменных действует внутри функции. Примеры статических связей рассматривались выше при описании функций и λ-функций.

В «Лиспе» имеется возможность использования динамических переменных при помощи директивы:

(DEFVAR переменная &OPTIONAL начальное значение).

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

Если переменная статическая, то можно ее использовать

вкачестве формального параметра на ступени выше, но даже не

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

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

Одно имя может обозначать разные переменные.

42

Рассмотрим примеры использования статических и динамических переменных для одного и того же символа:

>(setq х 2) 2

>(defun fn (х) …) FN

>(fn 'x)

В первом вызове назначается динамическая переменная со значением 2. Во втором х является формальным параметром, а значит, статической переменной. В третьем вызове функция FN может быть именем как статической, так и динамической (глобальной) переменной.

1.18. Обзор функциональных сред разработки программ

Существует множество разновидностей и реализаций языка Lisp, но наиболее удобной и понятной является версия XLispIDE. Данный редактор состоит из собственно редактора и отладчика.

Редактор XLispIDE имеет интуитивно понятный интерфейс, как показано на рис. 3.

Рис. 3. Интерфейс редактора XLispIDE

43

Имеются следующие закладки основного меню: File, Edit, Templates, Run, Settings и Help.

File (работа с файлами) состоит: из New – создание нового файла; Open – открытие файла lisp; Save – сохранение файла; SaveAs – сохранить файл как …; PrintSetup – установки принтера; Print – печать;

Exit – выход.

Edit (редактирование) состоит: из Undo – откат;

Redo – повтор;

Cut – вырезать фрагмент текста; Copy – копировать фрагмент текста; Paste – вставить фрагмент текста; Search (поиск) состоит:

из Find – найти по тексту; Replace – заменить по тексту.

View – отображение в редакторе соответствующих панелей. Settings – установки редактора.

Windows – открытые и закрытие окон. Help – помощь.

Возможно работать в отладчике (режим диалога), напри-

мер xlisp-plus (рис. 4).

Рис. 4. Режим диалога xlisp-plus

44

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

Если после «>» нажать стрелку вверх, то появится предыдущая команда.

Существуют еще различные онлайн-сервисы для использования компилятора Lisp. Например: https://commonlisp.net/downloads (рис. 5).

Рис. 5. Онлайн-сервис common-lisp

В чем лучше работать – редакторе, отладчике или он- лайн-сервисе? Ответ на данный вопрос зависит от задачи, которая стоит перед разработчиком, и от умения работать с каждой из сред разработки. При этом язык Lisp неизменен практически везде. Он может только отличаться наличием дополнительных команд или библиотек, но синтаксис всегда один и тот же.

45

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

Задача 1.1

Проделайте следующие вычисления с помощью интерпретатора «Лиспа»:

А) 3.234 * (45.6 + 2.43) = 155.32902, Б) 55 + 21.3 + 1.54*2.5432 – 32 = 48.216528, В) (34 – 21.5676 – 43) / (342 + 32*4.1) =

= – 0,064597633136094674556213017751479.

Задача 1.2

Запишите последовательности вызовов CAR и CDR, выделяющие из приведенных ниже списков символ «а». Упростите эти вызовы с помощью функций C…R.

А) (1 2 3 а 4) Б) (1 2 3 4 а)

В) ((1) (2 3) (а 4)) Г) ((1) ((2 3 а) (4))) Д) ((1) ((2 3 а 4)))

Е) (1 (2 ((3 4 (5 (6 а))))))

Задача 1.3

Определите значения следующих выражений:

А) ‘(+ 2 (* 3 5)) Б) (+ 2 ‘(* 3 5)) В) (+ 2 (’ * 3 5)) Г) (+ 2 (* 3 ’5)) Д) (quote ‘quote) Е) (quote 6)

Задача 1.4

Каково значение каждого из следующих выражений:

А) (ATOM (CAR (QUOTE ((1 2) 3 4)))); Б) (NULL (CDDR (QUOTE ((5 6) (7 8)))));

В) (EQUAL (CAR (QUOTE ((7)))) (CDR (QUOTE (5 7))));

46

Задача 1.5

Имеется список (первый второй третий четвертый). Вывести на экран второй элемент списка.

Задача 1.6

1. Напишите с помощью композиции условных выражений функции от четырех аргументов AND4 (x1 x2 x3 x4) и OR4 (x1 x2 x3 x4), совпадающие с функциями AND и OR от четырех аргументов.

Задача 1.7

Написать условное выражение (используя COND), которое дает NIL, если L – атом, и T – в противном случае.

Задача 1.8

Запрограммируйте с помощью предложения DO функцию «факториал».

Задача 1.9

Написать условное выражение (используя COND), которое выдает для списка L, состоящего из трех элементов, первый из этих трех, который является атомом, или список, если в списке нет элементов атомов.

1.20.Принцип работы некоторых базовых функций

ипредложений

Функция CAR возвращает в качестве значения первый элемент списка.

Вызов функции CAR с аргументом (a b c d) без апострофа был бы проинтерпретирован как вызов функции «a» с аргументом «b c d» и было бы получено сообщение об ошибке.

Функция CAR имеет смысл только для аргументов, являющихся списками:

(CAR ‘a) Error.

Функция CDR определена только для списков:

47

>(CDR ‘(a b c d)) (B C D)

>(CDR ‘((a b) c d)) (C D)

>(CDR ‘(a (b c d))) ((B C D))

>(CDR ‘(a)) NIL >(CDR NIL) NIL >(CDR ‘a) Error

Функция CONS строит новый список из переданных ей в качестве аргументов головы и хвоста:

(CONS голова хвост).

Для того чтобы можно было включить первый элемент функции CONS в качестве первого элемента значения второго аргумента этой функции, второй аргумент должен быть списком. Значением функции CONS всегда будет список:

>(CONS ‘a ‘(b c)) (ABC)

>(CONS ‘(a b) ‘(c d)) ((A B) C D)

>(CONS (+ 1 2) ‘(+ 3)) (3 + 3)

>(CONS ‘(a b c) NIL) ((A B C))

>(CONS NIL ‘(a b c)) (nil A B C)

Предложения COND можно комбинировать: (COND ((>x 0) (SETQ рез x))

((<x 0) (SETQ x –x) (SETQ рез х)) ((= х 0))

(Т ‘ошибка))

48

Предложение IF:

(IF (> x 0) (SETQ y (+ y x)) (SETQ y (– y x))).

Если выполняется условие х > 0, то к значению y прибавляется значение х, иначе от y отнимается значение х.

1.21.Дополнительные конструкции и механизмы функционального программирования

Многие версии «Лиспа» обладают функциями императивного программирования. Для этого служат функции передачи управления.

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

(PROG (ml m2 … mN) оператор1 оператор2 … операторМ).

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

Операторы предложения PROG вычисляются слева направо (сверху вниз), пропуская метки перехода.

Если какая-нибудь форма оператора является атомом (кроме служебных слов t и nil), то это считается меткой перехода, и на нее можно передать управление функцией GO:

(GO метка).

GO не вычисляет значение своего аргумента.

Оператор окончания вычисления и возврата значения записывается так:

(RETURN результат).

Оператор RETURN прекращает выполнение предложения PROG. Предложение PROG также является функцией и должно

49

иметь значение, в качестве которого выводится значение аргумента оператора PROG.

Если оператор RETURN был пропущен, то после вычисления его последнего оператора значением всего предложения

PROG станет NIL.

В «Лиспе» есть еще циклические предложения:

(LOOP m1 m2 …).

Данная функция реализует цикл, в котором формы m1, m2, … вычисляются до бесконечности, пока в какой-нибудь из вычисляемых форм не встретится явный оператор завершения

RETURN.

Для реализации цикла с параметрами, т.е. цикла на заданное число повторений служат формы DOTIMES и DOLIST.

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

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

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

50