- •Функциональное и логическое программирование
- •Глава 1. Классификация языков и стилей программирования 3
- •Глава 2. Программирование на языКе лисп 16
- •Глава 3. Программирование на языке пролог 89
- •Глава 1. Классификация языков и стилей программирования
- •Основные парадигмы программирования
- •Процедурное (императивное, директивное) программирование.
- •Объектно-ориентированное программирование.
- •Декларативное программирование
- •Логическое программирование.
- •Функциональное программирование.
- •Классификация языков программирования
- •Функциональные языки
- •Логические языки
- •Глава 2. Программирование на языКе лисп
- •История создания языка Лисп
- •Диалекты языка Лисп
- •Лисп-машины
- •Область применения языка Лисп
- •Особенности языка Лисп Одинаковая форма данных и программ
- •Хранение данных, не зависящее от места
- •Автоматическое и динамическое управление памятью
- •Функциональная направленность
- •Динамическая проверка типов
- •Интерпретирующий и компилирующий режимы работы;
- •Пошаговое программирование
- •Единый системный и прикладной язык программирования
- •Основы языка Лисп
- •Понятие функции.
- •Quote блокирует вычисления
- •Базовые функции языка.
- •Функция car возвращает в качестве значения первый элемент списка.
- •Функция cdr - возвращает в качестве значения хвостовую часть списка, т. Е. Список, получаемый из исходного списка после удаления из него головного элемента:
- •Функция cons включает новый элемент в начало списка:
- •Предикат equal проверяет идентичность записей:
- •Предикат equalp проверяет наиболее общее логическое равенство:
- •Другие простейшие встроенные функции Лиспа. Функция null проверяет, является ли аргумент пустым списком:
- •Комбинации вызовов car и cdr позволяют получить любой элемент списка:
- •Наиболее общая функция, выделяющая n-й элемент списка (при этом индексация начинается с 0):
- •Функция last позволяет выделить последний элемент списка:
- •Функция list - создает список из элементов:
- •Функция setq – невычисляющее присваивание:
- •Функция setf - обобщенная функция присваивания:
- •Функции, обладающие побочным эффектом, называются псевдофункциями.
- •С символом можно связать именованные свойства:
- •Функция get - возвращает значение свойства, связанного с символом:
- •Псевдофункция remprop удаляет свойство и его значение:
- •Вызов интерпретатора eval вычисляет значение выражения.
- •Ввод и вывод.
- •Использование файлов.
- •Определение функций
- •Задание параметров в лямбда-списке.
- •Передача параметров и область их действия.
- •Вычисления в лисПе.
- •Предложение let создает локальную связь внутри формы:
- •Последовательные вычисления.
- •Разветвление вычислений.
- •Циклические вычисления.
- •Передача управления.
- •Другие циклические структуры.
- •Динамическое управление из другого контекста
- •Внутреннее представление списков
- •Функциональное программирование Рекурсия. Различные виды рекурсии.
- •Функции более высокого порядка.
- •Применяющие функционалы.
- •Отображающие функционалы.
- •Макросы
- •Типы данных
- •Основные типы данных:
- •Глава 3. Программирование на языке пролог
- •Общие сведения о языке Пролог. Язык Пролог как система, реализующая логический вывод в исчислении предикатов первого порядка.
- •Фразы Хорна как средство представления знаний.
- •Синтаксис языка Пролог.
- •Фразы, термы, факты, правила.
- •Константы:
- •Простейшая программа на Прологе
- •Выполнение запроса в Прологе.
- •Неудача запроса и возврат назад.
- •Декларативная и процедурная семантика Пролога.
- •Рекурсивные процедуры.
- •Списки.
- •Операторы.
- •Средства управления ходом выполнения программы. Предикат сократить (отсечение).
- •Отрицание как неудача запроса.
- •Встроенные предикаты.
- •Типы отношений.
- •Ограничения, обеспечивающие целостность отношений.
- •Свойства отношений.
- •Подходы к программированию на языке Пролог.
Использование файлов.
В Коммон-ЛИСПе ввод-вывод осуществляется независимо от конфигурации внешних устройств через потоки. У системы есть несколько стандартных потоков, которые являются значениями глобальных переменных. Системные переменные *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
Вычисление лямбда-вызова (или лямбда-преобразование) производится по следующему алгоритму:
вычисляются фактические параметры и с их значениями связываются соответствующие формальные параметры;
вычисляется тело лямбда-выражения и полученное значение возвращается в качестве значения лямбда-вызова;
формальным параметрам возвращаются связи, существовавшие перед лямбда-преобразованием.
Лямбда-вызовы можно свободно объединять между собой и другими формами. Вложенные лямбда-вызовы можно ставить как на место тела лямбда-выражения, так и на место фактических параметров:
((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
