Россия, г. Магнитогорск |
Язык РЕФАЛ: Рефал-5
Рефал-5
Синтаксические отличия
Рефал-5 является современной версией Рефала, введенной автором в конце прошлого века. Некоторые отличия являются только синтаксическими и связаны с тем, что язык стал использоваться с современными операционными системами (в частности, с Windows). Перечислим их сначала, а затем перейдем к описанию новых структурирующих конструкций, делающих язык еще более лаконичным и мощным.
- Литеры, как и прежде, записываются в апострофах, но символы-метки записываются без ограничителей (косой черты) или в двойных кавычках.
- Числа (ранее символы-числа) представляют собой последовательность цифр со знаком "+" или "-" либо без знака.
- Переменные начинаются со строчной буквы s, t или e, за которой следует индекс - цепочка букв, цифр и знаков "-", "_". s -переменные имеют значением символы, t -переменные - термы, а e -переменные - любые выражения, включая пустые. Точка может отделять первую букву переменной от ее индекса.
- Левая часть рефал-предложения называется образцом , а правая часть - результатным выражением . Каждое рефал-предложение заканчивается точкой с запятой.
-
Определение функции имеет следующий синтаксис:1Это определение в дальнейшем будет дополнено конструкцией с образцовым блоком.
имя-функции { последовательность рефал-предложений };
- Вызов функции записывается в уголковых скобках "<" и ">", т.е. функциональными скобками являются уголковые.
-
Комментарии записываются одним из следующих способов:
- строка, начинающаяся со знака "*";
- окончание строки после "//";
- между "/*" и "*/".
- Входом в программу является всегда функция GO, с которой начинается выполнение.
- Директив нет. Внешние функции перечисляются после ключевого слова $EXTERNAL (или $EXTERN, или $EXTRN ). Функция, являющаяся входом (используется в других модулях), описывается после ключевого слова $ENTRY.
В качестве примера оформления модулей в Рефале-5 рассмотрим приведенное ниже описание функции факториал Fact в модуле M1 и ее вызов в модуле M2:
Файл M1.REF: * Функция Fact вычисляет произведение чисел от 1 до sN * Здесь используются краткие обозначения для * встроенных арифметических функций $ENTRY Fact { 0 = 1; sN = <* sN <Fact <– sN 1>>>; }; Файл M2.REF: * Головной модуль для вызова функции Fact * Здесь используются расширения Рефала, * а также встроенные функции ввода-вывода * и преобразований символов $EXTERN Fact; $ENTRY GO { , <PRINTLN 'Введи число:'> , <READ_LINE> : { = ; e1 = <PRINTLN 'Fact(' e1 ') = ' <Fact <NUMB e1>>> <GO>; }; };
В модуле М1 используются стандартные арифметические функции "*" для умножения и "-" для вычитания (перечисление стандартных функций в Рефале-5 не требуется).
В модуле M2 ввод числа функцией READ\_LINE и вычисление факториала от этого числа повторяется (вызов функции GO ). Окончанием работы программы является ввод пустой строки. Введенное число преобразуется из символьного представления в числовое стандартной функцией Numb, затем вызывается внешняя функция Fact вычисления факториала от введенного числа, после чего следует вывод строки:
Fact ( значение_числа ) = значение_факториала:
Условие
Введение условия в рефал-предложение позволяет увеличить мощь Рефала за счет значительного сокращения написания дополнительных функций.
Для того чтобы продемонстрировать смысл и значение вводимой конструкции, мы сначала рассмотрим пример:
требуется написать функцию CBD, которая из входной строки выделяет ее начало, ограниченное одним из некоторого множества символов: {"|", ",", ";"}.
Для решения этой задачи требуется во входной строке искать слева направо первый из указанного множества символов. Можно было бы для каждого символа из указанного множества написать свое рефал-предложение, но неясно, какой из них является в строке первым. К тому же в примере множество содержит только 3 символа, но если значительно расширить множество, то придется в решение вставлять очень много рефал-предложений, т. е. получить плохое неструктурированное решение. Поэтому будем решать задачу поэтапно.
Прежде всего заметим, что задача нахождения в строке терма из некоторого множества является достаточно общей и полезно написать функцию Include ("содержит"), которая узнает, содержит ли данный список термов заданный терм:
Include { (e1 tA e2) tA = T; (e1) tA = F; };
Обращение к ней имеет вид: <Include (eL) tA>. Результатом является символ-слово T, если список eL содержит терм tA, а в противном случае - символ-слово F. Структурные скобки позволяют улучшить понятность выражения-аргумента этой функции.
Теперь вернемся к основной функции CBD. Можно по очереди рассматривать символы строки и проверять, содержится ли очередной символ в указанном множестве, для чего вызывать функцию Include. Анализ результата вызова Include можно поручить какой-либо вспомогательной функции, скажем CBDO, которая в случае отрицательного результата (значение F ) снова вызовет функцию CBD для анализа уже следующего символа, а в случае положительного результата (значение T ) удалит остаток строки и закончит работу. Решение это будет выглядеть следующим образом:
CBD { sA eL = <CBDO sA eL <Include ('|,;') sA> >; = ; }; CBDO { sA e1 F = sA <CBD e1>; sA e1 T =; };
Такое решение, связанное с взаимной рекурсией двух функций, недостаточно наглядно. Хотелось бы в рефал-предложение функции CBD ввести условие так, чтобы в случае его выполнения (символ не принадлежит указанному множеству), мы могли бы продолжить проверку следующего такого символа за счет рекурсивного обращения. В случае же невыполнения условия (очередной символ принадлежит указанному множеству) мы должны закончить анализ и отбросить дальнейшую часть строки. Это решение имело бы следующую структуру:
CBD { sA eL, [sA не принадлежит множеству] = sA <CBD eL>; eL = ; };
Определим теперь синтаксис условия. Условие записывается после образца в виде:
, Er : Ep,
где Er - произвольное результатное выражение, а Ep - произвольный образец. В одном рефал-предложении может быть несколько условий. Выражение Er может содержать переменные, которые уже встречались в этом же предложении слева от запятой. Образец Ep может содержать как старые, так и новые переменные. Условия "работают" последовательно, после того как успешно завершится основное сопоставление, следующим образом:
- Значения старых переменных подставляются в Er и Ep, и в результате получаются рабочее выражение E'r и новый образец E'p.
- Затем E'r вычисляется, и результат вычислений - объектное выражение E'r - сопоставляется с образцом E'p.
- В случае успеха новые переменные с их значениями добавляются к списку старых переменных, и продолжается вычисление предложения (следующих условий или правой части).
- В случае неуспеха работа переходит на предыдущий образец: последний вариант сопоставления отвергается, и продолжается поиск других вариантов.
Перепишем функцию CBD с использованием условия:
CBD { sA eL, <Include ('|,;') sA> : F = sA <CBD eL>; eL = ; };
Заметим, что второе предложение объединяет два случая: когда строка начинается с разделителя и когда она пуста.
Мы можем еще упростить эту функцию, используя правило отождествления слева направо, при котором будет автоматически искаться первый символ, принадлежащий указанному множеству:
CBD { e1 sA e2, <Include ('|,;') sA> : T = e1; e1 = e1; };
Последний вариант иллюстрирует возможность того, что неуспех в условии не отвергает сразу предложение, а вызывает переход к следующему варианту сопоставления в предыдущем образце.