Ярославский Государственный Университет им. П.Г. Демидова
Опубликован: 06.11.2008 | Доступ: свободный | Студентов: 987 / 62 | Оценка: 4.50 / 4.00 | Длительность: 10:47:00
Лекция 6:

Язык РЕФАЛ: Рефал-5

< Лекция 5 || Лекция 6: 12 || Лекция 7 >
Аннотация: Синтаксические отличия Рефала-5 от Рефала-2; пример программы n! на Рефале-5. Условие в рефал-предложении Рефала-5: пример проблемы недостаточной наглядности при организации взаимной рекурсии двух функций; синтаксис условия и его семантика; решение проблемы недостаточной наглядности введением условия в рефал-предложение; примеры использования условия в функции сложения Add и решении задачи о назначениях. Блок как структурирующая конструкция Рефала-5: улучшение наглядности программы; пример сортировки слиянием двух упорядоченных последовательностей; образцовый окончание и образцовый блок рефал-предложения; результатный блок и результатное окончание рефал-предложения; пример функции сравнения множеств.
Ключевые слова: Рефал, литеры, апостроф, символ-метка, числа, символ-число, переменные, терм, рефал-предложение, образец, результатное выражение, определение функции, вызов функции, Функциональные скобки, комментарии, вход в программу, внешняя функция, external, EXTRN, вход, Entry, функция факториала, fact, арифметическая функция, вычитание, символьное представление, условие, include, Структурные скобки, CBD, очередной символ, вспомогательная функция, взаимная рекурсия, синтаксис условия, переменная условия, отождествление слева направо, результат, 3 вида условий, чистые условия, чистые присваивания, условные присваивания, digitize, CAR, обращение к функции, рекурсия, assign, рекурсивный вызов, интерпретация переменных, блок, семантика блока, merge, comparator, фигурные скобки, окончание, окончание блока, образцовое окончание, образцовй блок, тело функции, образцовый блок, полнота, результатная часть условия, результатное окончание, результатный блок, результатные выражения, разность, цепочка символов

Рефал-5

Синтаксические отличия

Рефал-5 является современной версией Рефала, введенной автором в конце прошлого века. Некоторые отличия являются только синтаксическими и связаны с тем, что язык стал использоваться с современными операционными системами (в частности, с Windows). Перечислим их сначала, а затем перейдем к описанию новых структурирующих конструкций, делающих язык еще более лаконичным и мощным.

  1. Литеры, как и прежде, записываются в апострофах, но символы-метки записываются без ограничителей (косой черты) или в двойных кавычках.
  2. Числа (ранее символы-числа) представляют собой последовательность цифр со знаком "+" или "-" либо без знака.
  3. Переменные начинаются со строчной буквы s, t или e, за которой следует индекс - цепочка букв, цифр и знаков "-", "_". s -переменные имеют значением символы, t -переменные - термы, а e -переменные - любые выражения, включая пустые. Точка может отделять первую букву переменной от ее индекса.
  4. Левая часть рефал-предложения называется образцом , а правая часть - результатным выражением . Каждое рефал-предложение заканчивается точкой с запятой.
  5. Определение функции имеет следующий синтаксис:
    имя-функции { последовательность рефал-предложений };
    1Это определение в дальнейшем будет дополнено конструкцией с образцовым блоком.
  6. Вызов функции записывается в уголковых скобках "<" и ">", т.е. функциональными скобками являются уголковые.
  7. Комментарии записываются одним из следующих способов:
    • строка, начинающаяся со знака "*";
    • окончание строки после "//";
    • между "/*" и "*/".
  8. Входом в программу является всегда функция GO, с которой начинается выполнение.
  9. Директив нет. Внешние функции перечисляются после ключевого слова $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 может содержать как старые, так и новые переменные. Условия "работают" последовательно, после того как успешно завершится основное сопоставление, следующим образом:

  1. Значения старых переменных подставляются в Er и Ep, и в результате получаются рабочее выражение E'r и новый образец E'p.
  2. Затем E'r вычисляется, и результат вычислений - объектное выражение E'r - сопоставляется с образцом E'p.
  3. В случае успеха новые переменные с их значениями добавляются к списку старых переменных, и продолжается вычисление предложения (следующих условий или правой части).
  4. В случае неуспеха работа переходит на предыдущий образец: последний вариант сопоставления отвергается, и продолжается поиск других вариантов.

Перепишем функцию CBD с использованием условия:

CBD {
        sA eL, <Include ('|,;') sA> : F = sA <CBD eL>;
        eL = ;
    };

Заметим, что второе предложение объединяет два случая: когда строка начинается с разделителя и когда она пуста.

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

CBD {
        e1 sA e2, <Include ('|,;') sA> : T = e1;
        e1 = e1;
    };

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

< Лекция 5 || Лекция 6: 12 || Лекция 7 >