Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Стили и методы программирования. Учебное пособие для СПО
.pdf
предыдущего образца. Если после второго образца идут фигурные
скобки, то мы задаем безымянную функцию внутри функции. Если же
их нет, то это отождествление рассматривается как дополнительное
условие успешности первого отождествления. Эта иерархия
вложенности может продолжиться на несколько уровней, причем
переменные внешних уровней на внутренних уровнях остаются
связанными, уже не изменяя значений.
Иерархически вложенные функции и условия в принципе не нужны для
Рефала, но их использование позволяет сократить и, главное, лучше
структурировать текст программы.
$ENTRY Go{=<Init>;}
$EXTRN Xxout;
* Инициализация поля зрения и констант
Init{=<Open 'r' 1 'Input.txt'><Trans <Acquire(<Get 1>)";}
Acquire {
e.Got ()= e.Got;
* Конец ввода - пустая строка
e.Got (e.New)=<Acquire e.Got e.New (<Get 1>)>;
}
Brackets {=('()')('[]')('')('<>');}
Trans {
e.A =<Result <Pairing () e.A > >;
}
Pairing {
* В первой скобке содержится последовательность всех незакрытых
* скобок вместе с сегментами данных, подлежащими помещению
* в даннуюпару скобок;
* каждый сегмент данных также заключен в скобки
(e.Unclosed (e.LastUn)(s.Lbrack e.Middle)) s.Rbrack e.Last,
<Brackets>: e.A(s.Lbrack s.Rbrack ) e.B =
* Встретилась правая скобка, парная последней незакрытой;
* выбрасываем отработанный сегмент из поля зрения
<Pairing (e.Unclosed (e.LastUn (s.Lbrack e.Middle s.Rbrack))) e.Last>;
((s.Lbrack e.Middle)) s.Rbrack e.Last,
<Brackets>: e.A(s.Lbrack s.Rbrack ) e.B =
* Парная незакрытой, находящейся внутри другой незакрытой
(s.Lbrack e.Middle s.Rbrack) <Pairing () e.Last>;
Стили и методы программированияНепейвода Н.Н.
81

(e.Unclosed (e.LastUn)(s.Lbrack e.Middle)) s.Rbrack e.Last,
<Brackets>: e.A(s.Lbrack1 s.Rbrack ) e.B =
* Непарные скобки
<Prout "Brackets Mismatch!"> Error;
(e.Unclosed ) s.Lbrack1 e.Last,
<Brackets>: e.A(s.Lbrack1 s.Rbrack ) e.B =
* Еще одна открывающая скобка; создаем новую группу данных
<Pairing (e.Unclosed (s.Lbrack1)) e.Last>;
() s.Lbrack e.Last,
<Brackets>: e.A(s.Lbrack s.Rbrack ) e.B =
* Первая открывающая скобка
<Pairing ((s.Lbrack)) e.Last>;
() s.Rbrack e.Last,
<Brackets>: e.A(s.Lbrack s.Rbrack ) e.B =
* Первая скобка - закрывающая
<Prout "Extra right bracket"> Error;
* Нейтральный символ вне скобок
() s.Other e.Last = s.Other <Pairing () e.Last>;
* Выражение и скобки исчерпаны - успех
() =;
* Выражение исчерпано, а скобки - нет
(e.Unclosed (e.Lastun))=<Prout "Not all brackets are closed" Error;
* Нейтральный символ в очередной скобке
(e.Unclosed (e.Lastun))s.Other e.Last=
<Pairing (e.Unclosed(e.Lastun s.Other)) e.Last>;
}
Result {
* Если была ошибка, выйти
e.A Error=;
* Иначе вывести результат для дальнейшего использования
e.A =<Xxout ('output.rdt') e.A>;
}
Листинг 5.4.1. Мультискобочное выражение: Рефал
Для того, чтобы нагляднее увидеть влияние стиля на программные
решения, сравните эту программу с развитием программы в
традиционном стиле, приведенным в § 11.5 книги [22]
. Данная
программа намного выразительнее, короче, легче модифицируема и не
Стили и методы программированияНепейвода Н.Н.
82

менее эффективна, чем программа 11.5.3 из указанного параграфа.
В практике работы со студентами автору приходилось сталкиваться с
ситуацией, когда программа для одной и той же задачи, написанная на
традиционном языке, была более чем в пять раз длиннее программы на
Рефале. Так что использование специализированных инструментов
окупается (естественно, когда они применены в подходящих случаях).
Из литературы по языку Рефал можно рекомендовать учебные пособия
[37]
, первое из которых имеется в общедоступном русском переводе, в
частности на сайте ссылка: http://www.refal.ru/diaspora.html.
1)
Здесь слово 'структура' понимается не в узко программистском, а в
научном и математическом смысле.
2)
Здесь у языка Рефал общая особенность с языками Prolog и LISP!
3)
Заметьте тонкую разницу между словами 'формироваться' и
'задаваться'! Имена функций заданы статически, но конкретное имя из
заданного конечного множества функций может быть сформировано
динамически.
4)
Изменение, введенное в ныне работающую реализацию Refal-PZ. В
учебнике Турчина двойные кавычки являются альтернативным
вариантом ограничителя строк.
5)
К этой концептуальной конфетке еще бы красивую обертку!
Стили и методы программированияНепейвода Н.Н.
83

Сентенциальное программирование: PROLOG
Исходные концепции языка. Данные. Управление при помощи неудач и
возвратов. Порождение программы в ходе исполнения. Стандартные
операции.
Общие концепции
Язык логического программирования PROLOG представляет собой одну
из моделей сентенциального программирования. Мы используем лишь
те возможности языка PROLOG, которые согласуются с принятым в
1996 г. стандартом [36]
. Как руководство по программированию на
языке PROLOG можно использовать, скажем, книгу [6]
. В ней
содержится наименьшее число недочетов и откровенных ошибок, а
также наибольшее число практических советов по сравнению с другими
известными автору пособиями.
Первой находкой создателей языка PROLOG явилось понятие
унификации, изобретенное в методе резолюций для доказательства
формул классической логики предикатов.
Два выражения называются унифицируемыми, если они могут быть
приведены к одному и тому же виду подстановкой значений вместо
свободных переменных. Унификация —вид конкретизации, при
котором границы всех синтаксических единиц фиксированы, структура
выражения однозначно определена и подстановка, приводящая два
выражения к одному и тому же виду, вычисляется рекурсивно.
Второй находкой, перенесенной авторами языка PROLOG из
специализированных программ (для логики и искусственного
интеллекта) в языки программирования, стала система обработки
неудач. Успешно произведенная унификация является лишь
разрешением выполнить некоторое действие. После проверки других
условий, возможно, мы будем вынуждены вернуться и выбрать другой
вариант.
Третья находка языка PROLOG, перенесенная в программирование из
метода резолюций, — это стандартизация цели. Целью доказательства в
методе резолюций всегда является получение пустого дизъюнкта, то
Стили и методы программированияНепейвода Н.Н.
84

есть стирание доказываемого выражения (с логической точки зрения,
приведение его к абсурду). Точно так же и в языке PROLOG: успешное
исполнение программы означает стирание поля зрения.
Четвертая находка создателей языка PROLOG взята из ограничения
классической логики. Хорновские формулы
обладают важным свойством. Для нахождения вывода в системе
хорновских формул достаточно производить так называемую линейную
резолюцию, когда на каждом шаге делается вывод из исходной формулы
и наследника цели. Никаких сочетаний исходных формул между собой
либо различных вариантов раскрытия цели между собой делать, в
принципе, не нужно.
Поле зрения, поле памяти и PROLOG-программа
Когда рассматривается исполнение программы в нетрадиционном
языке (например, PROLOG -программы), то естественно воспринимать
конкретную реализацию языка как новую машину нетрадиционной
архитектуры с высокоуровневыми командами (в данном случае как
PROLOG -машину).
Данные, используемые PROLOG -машиной, размещаются во всех частях
поля памяти и имеют общую структуру.
Рассмотрим на уровне абстрактного синтаксиса структуру данных,
обрабатываемых языком PROLOG. Все данные языка PROLOG
являются термами. Термы построены из атомов при помощи
функциональных символов. Атомами могут быть переменные и
константы, в свою очередь, делящиеся на имена и числа.
Функциональные символы являются именами и называются
функторами. Среди функторов выделяются детерминативы, которые в
реализации делятся на предикаты и встроенные функции (функции
обычно используются внутри выражений, а предикаты являются
основными единицами управления и обычно используются вне скобок
как основной функциональный символ выражения). Детерминативы
должны быть описаны в программе, а остальные функторы
Стили и методы программированияНепейвода Н.Н.
85

рассматриваются просто как структурные единицы и могут оставаться
неописанными.
В поле памяти выделяется поле зрения, содержащее непосредственно
обрабатываемые программой данные. Оно называется также целью и
состоит из последовательности термов.
Поле памяти имеет скрытую (при использовании стандартных
возможностей языка) часть, в которой прослеживается история
выполнения программы с тем, чтобы в случае необходимости
произвести обработку неудачи.
И, наконец, в поле памяти помещается сама PROLOG -программа,
которая естественно структурируется на две части, нередко
перемешанные в тексте самой программы, но обычно разделяемые при
использовании внешней памяти: база данных и база знаний.
База данных состоит из фактов, представляющих собой предикат,
примененный к термам.
База знаний состоит из предложений (клауз). Каждое предложение
имеет вид, подобный хорновской формуле
grandfather(X,Z) :- parent(X,Y), father(Y,Z).
Предложение состоит из головного выражения (соответствующего
заключению хорновской формулы) и его раскрытия: нескольких
выражений, соединенных как последовательно достигаемые подцели
(они соответствуют посылкам хорновской формулы)
1)
.
В любой момент исполнения программы база данных и база знаний
могут быть модифицированы.
Теперь перейдем к конкретному представлению данных.
В конкретном синтаксисе переменные языка представлены именами,
состоящими из букв и начинающимися с большой буквы либо с символа
подчеркивания _. Переменная _ называется анонимной переменной и
считается различной во всех своих вхождениях.
Стили и методы программированияНепейвода Н.Н.
86

Константы языка PROLOG2) в конкретном синтаксисе делятся на имена
(идентификаторы, начинающиеся с маленькой буквы либо совокупность
нескольких специальных символов типа <, =, $ ), символы и числа
(символы отождествляются с целыми числами, являющимися их
кодами). Произвольная последовательность символов может быть
сделана единой константой, например:
'C:\"SICS Prolog"\program.pl'.
Любое константное имя может служить функтором. Функторы
различаются арностью (т. е. количеством аргументов), таким образом,
может быть сразу несколько функторов с одним и тем же именем.
Например, write(a(1)) и write(a(1),file1) используют
различные функторы. Некоторые функции могут быть описаны как
операции (инфиксные, префиксные либо постфиксные). В отличие от
почти всех остальных языков, операции рассматриваются лишь как
сокращение для выразительности. Например, x+y означает в точности
то же, что и +(x,y).
Для некоторых из предопределенных в системе функций и предикатов
имеются дополнительные ограничения на аргументы
3)
. Например, в
функции load(f) f должно быть именем файла.
Поле зрения (цель), содержащее непосредственно обрабатываемые
программой данные, состоит из последовательности термов,
разделенных либо запятыми (в этом случае они понимаются как
"последовательно достигаемые подцели"), либо символами |, в этом
случае подцели "альтернативны".Наиболее важным и классическим
является случай последовательно достигаемых подцелей, через который
определяется и семантика альтернативных подцелей
4)
.
В конкретном представлении предложение, например,
grandfather(X,Z) :- parent(X,Y), father(Y,Z).
также рассматривается как терм, поскольку имена , (здесь символ
запятой — имя операции) и :- рассматриваются как инфиксные
операции, причем запятая связывает сильнее.
Стили и методы программированияНепейвода Н.Н.
87

Как правило, предложения, относящиеся к одному и тому же предикату,
группируются вместе, например:
parent(X,Y) :- mother(X,Y).
parent(X,Y) :- father(X,Y).
Порядок предложений существенен.
База данных состоит из фактов. Факты могут выражаться в одном из
двух видов. Во-первых, факт может рассматриваться как предложение,
немедленно приводящее к успеху (успех— это стирание целей). Поэтому
он может быть записан:
father(ivan,vasilij):-true.
Здесь мы встретились с одной из двух стандартных целей: true
обозначает очевидную удачу, а fail — очевидную неудачу.
Во-вторых, специально для фактов имеется скоропись, означающая то
же самое:
father(ivan,vasilij).
В принципе, все остальные структуры языка PROLOG выражаются
через элементарные, определенные выше. Но некоторые из структур
прагматически настолько важны, что получили отдельное оформление
и более эффективную реализацию. Это, прежде всего, списки и строки.
Список, в принципе, определяется как терм, построенный из других
термов и пустого списка [] применением двухместного функтора .
(head,tail). Выстроенная в стандартном порядке композиция
.(a,.(b,. . . , .(z,[]). . . ))
понимается как линейный список и обозначается [a,b,. . . ,z].
Для обозначения присоединения нескольких данных термов к началу
списка имеется стандартная операция
[t,u|L].
Строки рассматриваются как линейные списки кодов символов и
Стили и методы программированияНепейвода Н.Н.
88

обозначаются последовательностью символов, взятой в двойные
кавычки:
"Ну, получили то, что искали? Ответьте y или n."
Заслуживает упоминания механизм введения новых операций в язык
PROLOG. Каждый пользователь может определить свои собственные
унарные или бинарные операции или переопределить стандартные.
Приведем в качестве примера описания некоторых стандартных
операторов языка PROLOG:
:- op(1200,xfx, ':-').
:- op(1200,fx, [':-','?-']).
:- op(1000,xfy, ',').
:- op(700, xfx, [=,is,<,=<,==]).
:- op(500, yfx, [+,-]).
:- op(500, fx, [+,-,not]).
:- op(400, yfx,[*,/,div]).
Первый аргумент в этих описаниях — приоритет операции. Он может
быть от 1 до 1500. Второй аргумент — шаблон операции; x обозначает
выражение с приоритетом, строго меньшим приоритета операции; y —
выражение с приоритетом, который меньше или равен приоритету
операции, f — положение самого символа операции относительно
аргументов. Таким образом, шаблон yfx для операции - означает, что
выражение X-Y-Z понимается как (X-Y)-Z, шаблон xfy для запятой
означает, что t,u,r понимается как t,(u,r),шаблон xfx для :означает невозможность использования нескольких таких операций
подряд без дополнительных скобок. Операции с меньшими
приоритетами связывают свои аргументы сильнее. Один и тот же атом
может быть определен и как унарная, и как бинарная операция.
Пример описаний операций показывает, что даже локальное
использование различения конкретно- и абстрактно-синтаксических
представлений программы дает возможность получить большие
преимущества. В PROLOG не пришлось отдельно заниматься
семантикой операций, поскольку в абстрактном синтаксисе их нет.
Автоматически устраняются многие тонкие вопросы, связанные, в
частности, с возможностью PROLOG -программы преобразовывать
Стили и методы программированияНепейвода Н.Н.
89

саму себя (см. упр. 7).
Управление исполнением программы
Джулией Робинсон доказано (см., напр. [30]
), что для выражений
первого порядка имеется эффективный алгоритм унификации,
находящий для двух выражений унифицирующую подстановку либо
обосновывающий, что такой подстановки нет.
Пример 6.3.1. Две последовательности выражений
где a, b — константы, а латинские буквы из конца алфавита —
переменные, унифицируются в
подстановкой
А в двух последовательностях
никакие два соответственных выражения унифицированы быть не
могут.
Уже в приведенном примере видно, что унификация — глобальная
операция.
Заметим, что логический алгоритм унификации обладает свойством
частичного исполнения: если унифицировать две подструктуры, то
Стили и методы программированияНепейвода Н.Н.
90
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
