Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Учебное пособие Функциональное и логическое программирование.docx
Скачиваний:
4
Добавлен:
01.07.2025
Размер:
309.57 Кб
Скачать

Использование файлов.

В Коммон-ЛИСПе ввод-вывод осуществляется независимо от конфигурации внешних устройств через потоки. У системы есть несколько стандартных потоков, которые являются значениями глобальных переменных. Системные переменные *STANDARD-INPUT* и *STANDARD-OUTPUT* определяют для функций ввода и вывода файлы по умолчанию, которыми в начале сеанса является дисплей. Поэтому нет необходимости в начальном объявлении потока ввода-вывода для всех рассмотренных функций, кроме FORMAT.

Если мы хотим обмениваться данными с файлом, его надо сначала открыть в соответствующем режиме:

(OPEN файл &KEY :DIRECTION)

Значениями ключевого параметра :DIRECTION могут быть следующие ключи:

:INPUT открыть поток для чтения из файла;

:OUTPUT открыть для вывода в файл;

:IO открыть двусторонний поток.

Потоковый объект, полученный в результате вызова OPEN, можно присвоить какой-нибудь переменной для следующего использования:

(setq p (open ”proba.lsp” :direction :io))

Чтобы записанные в файл данные сохранились, необходимо закрыть файл:

(CLOSE поток)

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

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

(READ x &OPTIONAL поток)

(PRINT x &OPTIONAL поток)

(PRINT1 x &OPTIONAL поток)

(PRINTC x &OPTIONAL поток)

(TERPRI x &OPTIONAL поток)

(setq x (read p))

Определение функций

Определение функций и их вычисление в Лиспе основано на лямбда-исчислении Алонзо Чёрча. Алонзо Чёрч — выдающийся американский математик и логик, внесший значительный вклад в основы информатики. В языках программирования под «лямбда -исчислением» зачастую понимается механизм «анонимных функций»,  которые можно определить прямо в том месте, где они используются, и которые имеют доступ к локальным переменным текущей функции. Для объявления анонимных функций используется специальный синтаксис лямбда-выражений. Используя лямбда-выражения, можно объявлять функции в любом месте кода. В Лиспе лямбда-выражение имеет вид:

(LAMBDA (x1 x2 ... xn) fn).

Лямбда-выражение. Символ LAMBDA означает, что мы имеем дело с определением функции. Символы x1, х2, .. хn являются формальными параметрами определяемой функции. Формальность параметров означает, что их можно заменить на любые другие символы, и это не отразится на вычислениях, определяемых функцией. Список, образованный из параметров, называют лямбда-списком.

Телом лямбда-выражения fn является произвольная форма, значение которой может вычислить интерпретатор Лиспа:

(LAMBDA (x y) (+ x y))

Лямбда-вызов. Чтобы применить лямбда-выражение к некоторым аргументам, нужно в вызове функции поставить лямбда-выражение на место имени функции:

(лямбда-выражение а1 а2 ... аn),

здесь ai - формы, задающие фактические параметры.

Пример: ((LAMBDA (x y) (+ x y)) 1 2) => 3

Вычисление лямбда-вызова (или лямбда-преобразование) производится по следующему алгоритму:

  1. вычисляются фактические параметры и с их значениями связываются соответствующие формальные параметры;

  2. вычисляется тело лямбда-выражения и полученное значение возвращается в качестве значения лямбда-вызова;

  3. формальным параметрам возвращаются связи, существовавшие перед лямбда-преобразованием.

Лямбда-вызовы можно свободно объединять между собой и другими формами. Вложенные лямбда-вызовы можно ставить как на место тела лямбда-выражения, так и на место фактических параметров:

((LAMBDA (x) ((LAMBDA (y) (LIST x y)) ‘b)) ‘a) => (a b)

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

Дать имя и определить новую функцию можно с помощью функции:

(DEFUN имя лямбда-список тело)

Функция DEFUN связывает символ с лямбда-выражением, и символ начинает представлять определенные этим лямбда-выражением вычисления. Значением этой формы является имя новой функции.

После именования функции ее вызов осуществляется по имени и параметрам:

(DEFUN list1 (x y) (cons x (cons y nil))) => list1

(list1 ‘c ‘n) => (c n)

Как BOUNDP проверяет наличие у символа значения, так и FBOUNDP проверяет, связано ли с символом определение функции:

(FBOUNDP 'символ)

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

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

  • его имя;

  • значение, назначенное функцией присваивания (setq);

  • описание вычислений (лямбда - выражение), назначенное определением функции DEFUN.

  • список свойств.

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

  • функцию (родители х), которая возвращает в качестве значения список родителей;

  • предикат (сестры-братья х1 х2), который истинен в том случае, если х1 и х2 имеют общего родителя.

Решение.

(SETF (GET 'Иван 'мама) 'Оля) => Оля

(setf (get 'Иван 'папа) 'Коля) => Коля

(symbol-plist 'Иван) => (папа Коля мама Оля)

(setf (get 'Петр 'мама) 'Оля) => Оля

(setf (get 'Петр 'папа) 'Коля) => Коля

(symbol-plist 'Иван) => (папа Коля мама Оля)

(symbol-plist 'Петр) => (папа Коля мама Оля)

(defun родители (x) (list (get x 'мама) (get x 'папа))) => родители

(родители 'Сергей) => (nil nil)

(родители 'Иван) => (Оля Коля)

(defun сестры-братья (x1 x2)

(or (eq (get x1 'мама) (get x2 'мама))

(eq (get x1 'папа )(get x2 'папа)) ) ) => сестры-братья

(сестры-братья (Иван Петр) => t