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

Языки логического программирования. Учебное пособие

.pdf
Скачиваний:
1
Добавлен:
08.09.2026
Размер:
2 Мб
Скачать
☆
Язык РЕФАЛ: сложные конструкции Рефала-2
Спецификации переменных: синтаксис спецификации; спецификатор и его элементы; элементы-множества спецификатора; общий вид спецификатора; определение имени спецификатора и его использование. Структора рефал-программы и ее модулей: директива программы и ее части; директива комментария; ключевые слова директив; модульность и объявление входов и внешних имен; пример двухмодульной программы.
Спецификации переменных
В том случае, когда нужно ограничить множество значений, которое может принимать переменная (левой части рефал-предложения), в описание переменной вводится спецификация, которая должна находиться между типом переменной и индексом без пробелов. Спецификация может быть определена непосредственно в круглых скобках либо именем спецификатора (для предопределенных имен спецификаторов ). Например, переменная S('+-')z может принимать значения только знаков '+' или '-'. При этом цепочка символов '+-' определяется как спецификатор переменной Sz. В правой части рефал-предложения спецификация не пишется. Между скобками спецификации и спецификатором может быть любое количество пробелов.
Каждый спецификатор в общем случае представляет собой описание некоторого множества термов (допустимые значения переменной). Это описание строится из некоторого набора элементарных множеств, обозначения которых мы будем называть элементами. В качестве элементов могут быть использованы любые символы, а также следующие конечные множества:
S - множество всех символов; B - множество структурных термов; W - множество всех термов; F - множество символов-меток; N - множество символов-чисел; R - множество символов-ссылок;
Языки логического программированияВ.С. Рублев
21
O - множество символов-литер (знаков символов); L - множество прописных букв (латинских и русских); D - множество десятичных цифр.
В простом случае используется , которая представляет собой объединение множеств элементов будем называть набор опций, описывающих необязательные возможности.. Например, функция
Ident проверяет, является ли выражение аргумента идентификатором
(последовательностью больших букв и цифр, начинающейся с буквы), и возвращает символ 'T' при положительном ответе, а символ 'F' при отрицательном ответе.
Ident S(L)1 E(LD)2 = 'T' Ex = 'F'
Функция Ident при интерпретации первого рефал-предложения проверяет, является ли первый символ выражения аргумента буквой (для этого первый выделяемый символ S1 имеет спецификацию L) и являются ли все последующие символы, если они есть (переменная типа E может интерпретировать пустое значение), буквами или цифрами. Если это так, то возвращается символ 'T'. А если это не так, то второе рефал-предложение (всегда выполнимое) возвратит символ 'F', что и требовалось.
В общем случае спецификатор имеет вид:
P1(Q1)... Pn(Qn)P0,
где - произвольные цепочки элементов спецификатора (возможно, пустые). Если P0 - пустая цепочка, то
считается P0=W (множеству всех термов). Наличие цепочки элементов спецификатора в круглых скобках означает, что элементы множества
этой цепочки удаляются, а цепочка без круглых скобок ведет к добавлению в суммарное множество элементов множества этой цепочки. Поэтому множество спецификатора рассматривается как множество:
.
Языки логического программированияВ.С. Рублев
22
Так, например, переменная W(('ABC'))1 может принимать значения любых термов, кроме символов литер 'A', 'B', 'C', а переменная
S(('A')L('0')D)2 может принимать значения любых символов,
кроме буквы 'A' и цифры '0'.
Для спецификатора можно задать имя с тем, чтобы употреблять это имя вместо выражения спецификатора. Это делается описанием имени спецификатора:
<имя спецификатора> S <спецификатор>
Пусть, например, вводятся имена спецификаторов для операций типа сложения и для операций типа умножения:
ADDOP S '+-' MULTOP S '*/'
Теперь имена спецификаторов, ограниченные с обеих сторон символами двоеточие ":", можно употреблять в спецификациях. Например, переменная S:ADDOP:1 может принимать значения только символов операций типа сложения, а переменная
S((:ADDOP::MULTOP:)S)2 может принимать значения всех
символов, кроме символов арифметических операций.
Рассмотрим еще 1 пример: функция Separate отделяет от выражения аргумента самый большой идентификатор слева и заключает его в структурные скобки, оставляя неизменным оставшуюся часть выражения. Если же такого идентификатора нет, то функция помечает выражение символом '*'. Решение, основанное на функции Ident не годится, так как нужно знать, где заканчивается идентификатор. Однако если использовать отождествление справа налево, то мы получаем следующее простое решение.
Separate R S(L)1 E(LD)2 Ex = (S1 E2) Ex Ex = '*' Ex
Структура рефал-программы и ее модулей
Рефал-программа состоит из одного или нескольких модулей - частей
Языки логического программированияВ.С. Рублев
23
программы, которые компилируются отдельно. Каждый модуль программы представляет собой последовательную запись директив программы и комментариев. Каждый комментарий записывается в отдельной строке и начинается с литеры "*". Перед этим символом могут стоять пробелы в любом количестве, а после - любой текст, который игнорируется при выполнении программы.
Каждая директива находится в отдельной строке и имеет следующий вид:
[<идентификатор>] [<ключевое слово>] [<информация>]
Присутствие всех трех частей директивы необязательно. Между отдельными частями директивы должен быть хотя бы 1 пробел, но если идентификатор директивы отсутствует, то строка директивы начинается с пробела. Так, директива описания функции содержит только ее идентификатор и, возможно, информацию в виде рефал-предложения. Директива рефал-предложени я содержит только информацию. Директива спецификатора имеет идентификатор спецификатора, ключевое слово S и в качестве информации директивы сам спецификатор. Если все 3 компоненты директивы отсутствуют, то это пустая директива, которая используется для улучшения общего вида программы.
Ключевые слова директив приведены в табл. 3.1.
Таблица 3.1.
Ключевое
слово
Директива Примечание
START
Начало модуля(программы)
имя директивы модуля (программы)
END
Конец модуля (программы)
без имени
ENTRY
Объявление входов модуля
функции модуля, используемые в других модулях
EXTRN
Объявление внешних функций
функции других модулей, используемые в данном
R Рефал-предложение отождествление справа
Языки логического программированияВ.С. Рублев
24
L Рефал-предложение
отождествление слева (по умолчанию отсутствует)
S
Объявление имени спецификатора
Необязательное имя модуля (программы) дается директивой START, а конец модуля (программы) отмечается директивой END без имени (директива начинается с пробела).
Имена всех функций модуля, которые могут быть использованы в других программах и модулях, должны быть объявлены входами директивой
ENTER (без имени). Имена функций других модулей, используемых в
программе, должны быть объявлены внешними директивой EXTRN. Благодаря директиве EXTRN программа связывается с внешним миром, так как функции ввода исходных данных и вывода результатов
описываются на других языках1) (ассемблер, С). Модуль может не содержать директивы EXTRN, если он предназначен для использования в других модулях и программах (в этом случае директива ENTRY должна быть обязательно). Имена входов и внешних функций даются их идентификаторами. Но если в одном модуле используется идентификатор внешней функции, отличный от ее описания в другом модуле, то в скобках дается имя этой внешней функции в модуле ее описания.
Смысл директивы SWAP мы дадим при описании специальных видов памяти, с которой может работать программа.
Приведем пример двухмодульной программы.
M1 START ENTRY COMMUN EXTRN DREAM COMMUN E1 '+' E2 = k/DREAM/ E1. k/DREAM/ E2. END M2 START ENTRY DREAM EXTRN общение(COMMUN) DREAM Sx E1 = Sx k/общение/ E1.
Языки логического программированияВ.С. Рублев
25
END
Здесь функция модуля M1 с внутренним именем COMMUN имеет вызов в модуле M2 с внутренним именем "общение".
Каждое имя, которое употребляется внутри модуля, должно быть описано либо как имя внутренней функции или спецификатора, либо как имя внешней функции или спецификатора в директиве EXTRN.
Упражнения
1. Разработайте рефал-функцию, которая отделяет от выражения самый большой идентификатор справа и заключает его в структурные скобки.
2. Разработайте спецификатор, который ограничивает множество символов цифрами (кроме 0) и символами-числами.
3. Разработайте спецификатор, который ограничивает множество термов любыми термами кроме составных символов.
4. Разработайте рефал-функцию, которая реверсирует последовательность структурных и функциональных термов.
5. Разработайте двухмодульную программу, один из модулей которой содержит описание функций выделения в стуктурные скобки самого левого идентификатора, переменной, самого левого числа и самой левого символа арифметической операции, а другой модуль производит лексический анализ выражения, определенного аргументом у функции лексического анализа, функция которого описана с помощью функций первого модуля.
1)
Такие первичные функции мы опишем в следующем разделе.
Языки логического программированияВ.С. Рублев
26
Язык РЕФАЛ: первичные функции и примеры составления программ
Первичные функции Рефала-2: функции ввода/вывода; арифметические функции; функции лексического анализа; функции для работы с символами-метками. Синтаксический анализатор для языка арифметических выражений: описание синтаксиса языка формами Бэкуса-Наура; программа анализатора и ее близость к языку БНФ. Программа суммирования последовательности чисел и ее особенности.
Первичные функции
Во многих случаях возникает необходимость в функциях, которые не могут быть описаны средствами Рефала. К ним прежде всего относятся функции операций ввода/вывода, осуществляющие связь со внешним миром. Помимо этого, такими функциями являются ряд функций арифметических и логических операций, а также функции работы с особыми видами памяти Рефала. Эти функции пишутся обычно на других языках и называются первичными. С точки зрения работы Рефал-машины исполнение вызова первичной функции осуществляется за 1 шаг ее работы.
Функции ввода/вывода
Функции ввода/вывода предназначены для организации диалогового взаимодействия между пользователем и программой. Для ввода строки с клавиатуры используется функция card. Для вывода информации на экран используются операции print, prout, которые различаются возвращаемым значением и способом представления информации на экране. Ввод/вывод может быть переназначен в произвольный файл стандартным образом.
Функция card осуществляется вызовом
k/card/ .
и дает возможность читать строку символов из входного потока. После вызова функции программа переходит в состояние ожидания ввода
Языки логического программированияВ.С. Рублев
27
строки. По окончании ввода строки следует нажать клавишу "Enter". Возвращаемым значением является введенная строка.
Функция print осуществляется вызовом
k/print/ <E>.
где E - произвольное рефал-выражение (возможно, и пустое), и выводит c новой строки это выражение. При пустом выражении пропускается 1 печатная строка. При непустом выражении символы­литеры выводятся в виде соответствующих литер, структурные скобки выводятся как круглые скобки, а составные символы выводятся с ограничителями ' вместо "/". Возвращаемым значением является выводимое выражение.
Например, функция
k/print/ 'функция'(/f1/).
возвратит выражение
'функция'(/f1/).
При этом на устройство вывода будет выдана строка
'функция'('f1') ,
Функция prout осуществляется вызовом
k/prout/ <E>.
где E - произвольное рефал-выражение (возможно, и пустое), и выводит c новой строки это выражение. Отличием от функции print является то, что всегда возвращается пустое выражение.
Арифметические функции
Функции работают только с целыми числами, каковыми являются в представлении Рефала - символы-числа с возможно предшествующим знаком минус '-'. Например, '-'/7/, /0/, /2007/. Нуль
Языки логического программированияВ.С. Рублев
28
представляется либо нулевым символом-числом, либо пустым выражением.
Все арифметические функции допускают использование пустого выражения в качестве аргумента и воспринимают его как нуль.
К арифметическим функциям относятся:
add - сложение; mul - умножение; sub - вычитание; dr - деление нацело с остатком; div - деление нацело; p1 - инкремент (увеличение на 1); m1 - декремент (уменьшение на 1).
Для преобразования целых чисел в символьный вид и обратно используются функции symb и numb.
Функции add, mul, sub осуществляются вызовом
k/op/ (<N1>) <N2>. ,
где op - одна из операций add, mul, sub, а N1 и N2 - макроцифры. Этот вызов возвращает макроцифру (для положительного результата знак '+' не ставится) суммы, произведения или разности N1-N2 в зависимости от операции. Например, следующие вызовы функции приведут к таким результатам:
k/add/ ('-'/5/) /3/. -> '-'/2/ k/add/ () /2/. -> /2/ k/mul/ ('-'/5/) /3/. -> '-'/15/ k/mul/ (/2/). -> /0/ k/sub/ ('-'/5/) /3/. -> /8/ k/sub/ () /2/. -> '-'/2/
Функция dr осуществляется вызовом
k/dr/ (<N1>) <N2>. ,
Языки логического программированияВ.С. Рублев
29
где N1 - делимое, а N2 - делитель. Она возвращает выражение
<Q> (<R>) ,
где Q - частное, а R - остаток. Попытка делить на 0 приводит к аварийному завершению. Знаки частного и остатка определяются следующим образом: сначала производится деление нацело без учета знаков делимого и делителя, а затем частному и остатку приписываются знаки так, чтобы выполнялось соотношение:
N1 = Q*N2 + R
т. е. остаток (не равный нулю) всегда имеет знак делимого, а знак частного определяется знаками делимого и делителя (при одинаковых положителен, при разных отрицателен). Например, следующие вызовы функции приведут к таким результатам:
k/dr/ (/5/) /3/. -> /1/ (/2/) k/dr/ (/5/) '–'/3/. -> '–'/1/ (/2/) k/dr/ ('–'/5/) /3/. -> '–'/1/ '–'(/2/) k/dr/ ('–'/5/) '–'/3/. -> /1/ '–'(/2/)
Функция div в отличие от функции dr возвращает только частное. В остальном же подобна ей. Например, следующие вызовы функции приведут к таким результатам:
k/div/ (/5/) /3/. -> /1/ k/div/ (/5/) '–'/3/. -> '–'/1/ k/div/ ('–'/5/) /3/. -> '–'/1/ k/div/ ('–'/5/) '–'/3/. -> /1/
Функции p1, m1 имеют 1 аргумент и возвращают соответственно макроцифру, увеличенную на 1 или уменьшенную на 1. Например, следующие вызовы приведут к таким результатам:
k/p1/ '–'/2007/. -> '–' /2006/ k/p1/. -> /1/ k/m1/ /2007/. -> /2006/ k/m1/. -> '–'/1/
Языки логического программированияВ.С. Рублев
30
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]