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

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

.pdf
Скачиваний:
1
Добавлен:
08.09.2026
Размер:
2 Мб
Скачать
☆
Функция symb преобразует макроцифру (аргумент) в символьное представление. Например, следующие вызовы функции приведут к таким результатам:
k/symb/ '–'/2007/. -> '–2007' k/symb/. -> '0'
Функция numb преобразует цепочку символов (аргумент), являющуюся десятичной записью целого числа в макроцифру этого числа. Например, следующие вызовы функции приведут к таким результатам:
k/numb/ '–2007'. -> '–'/2007/ k/numb/ '0'. -> /0/
Функции лексического анализа
Функции предназначены для лексического разбора выражений. В табл.
4.1 приведены все 5 функций, их аргументы и назначение. В качестве
примеров рассмотрим следующие вызовы функции, которые приведут к таким результатам:
k/first/ /2/ 'A'('B')'C'. -> ('A'('B')) 'C' k/first/ /5/ 'A'('B')'C'. -> '*A'('B')'C' k/last/ /2/ 'A'('B')'C'. -> 'A' (('B')'C') k/last/ /5/ 'A'('B')'C'. -> 'A'('B')'C*' k/lengw/ 'A' () ('A'). -> /3/ 'A' () ('A') k/lengw/ . -> /0/ k/lengr/ 'A' () ('A'). -> /6/ 'A' () ('A') k/lengr/ . -> /0/ k/multe/ /5/ 'A'. -> 'AAAAA' k/multe/ /2/ 'A'('B'). -> 'A'('B')'A'('B')
Таблица 4.1.
Имя
функции
Выражение
аргумента
Назначение функции
Возвращаемое
значение
first <N> <E>
отщепляет от начала выражения E часть,
(E1) E2, где E = E1E2, указанную или '*'E, если длины
Языки логического программированияВ.С. Рублев
31
мало
last <N> <E>
отщепляет от конца выражения E часть, имеющую указанную длину N термов
E1 (E2), где E = E1E2, или E'*', если
длины мало
lengw <E>
определяет длину выражения в термах
N E, где N –
макроцифра
lengr <E>
определяет длину выражения в символах вместе со скобками
N E, где N –
макроцифра
multe <N> <E>
размножает выражение E в N экземплярах
EE...E если N = 0,
то пустое выражен.
Функции для работы с символами-метками
Иногда требуется превратить символ-метку в цепочку символов, из которой она состоит, и при этом, если одну и ту же символ-метку превращать в цепочки символов несколько раз, то требуется, чтобы получались одинаковые цепочки. Наоборот, иногда требуется превратить цепочку символов в символ-метку. При этом одинаковые цепочки должны превращаться в одну и ту же символ-метку. Наконец, полученную из цепочки символ-метку иногда требуется зарегистрировать в качестве имени функции. Этим целям служат приведенные в табл. 4.2 функции. В качестве примеров приведем следующие вызовы функции, которые дадут такие результаты:
k/ftochar/ /aaaa3434/. -> 'aaaa3434' k/ftochar/ /ABCD/. -> 'ABCD' k/chartof/ 'aaaa3434'. -> /aaaa3434/
k/chartof/ 'ABCD'. -> /ABCD/ k/functab/ /func1/. ->
Таблица 4.2.
Имя
функции
Выражение
аргумента
Назначение функции
Возвращаемое
значение
Языки логического программированияВ.С. Рублев
32
ftochar /<F>/
превращает символ-метку
/<F>/ в цепочку символов
цепочка символов
<F>
chartof <F>
превращает цепочку символов
F в символ-метку
символ-метка
/<F>/
functab /<F>/
регистрирует символ-метку
/<F>/
<пусто>
Примеры программ
Рефал является металингвистическим языком для формализации синтаксиса алгоритмических языков. В этом его основное назначение, но благодаря первичным арифметическим функциям возможна реализация и вычислительных алгоритмов. Мы рассмотрим 2 примера, показывающие возможности Рефала в этих двух направлениях.
Синтаксический анализатор для языка арифметических выражений
Рассмотрим упрощенный язык арифметических выражений, в котором введены всего 2 арифметические операции: сложение (+) и умножение (*), а также переменные, числа и скобки, определяющие порядок вычислений. Синтаксис языка определен следующими формами Бэкуса­Наура (БНФ):
<арифмвыр> ::= <арифмвыр>+<множитель>| <множитель> <множитель> ::= <множитель>*<первичное>| <первичное> <первичное> ::= (<арифмвыраж>)|<число>| <имяпеременной> <число> ::= <цифра>|<число><цифра> <имяпеременной> ::= <буква>|<имяпеременной><буква>| <имяпеременной><цифра> <цифра> ::= 0|1|2|3|4|5|6|7|8|9 <буква> ::= A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R| S|T|U|V|W|X|Y|Z|a|b|c|d|e|f|g|h|i|j|k|l|
Языки логического программированияВ.С. Рублев
33
p|q|r|s|t|u|v|w|x|y|z
Например, правильным арифметическим выражением является следующее
a+(2+c*d)*(d+(a+c)*2),
а выражение
a+(2+c*d*(d+)(a+c)*2)
не является правильным, так как его часть (d+)(a+c) не является множителем (выражение в первых скобках не является правильным арифметическим выражением - нет множителя после знака '+' и между первым и вторым первичными выражениями в скобках нет знака
'*' ).
Необходимо построить для этого языка синтаксический анализатор, который бы проверял вводимое арифметическое выражение и в случае правильности выдавал бы в качестве результата на экран строку "Выражение верно", а в случае ошибочности указывал бы ошибочное место и диагностику ошибки (например, "ошибка: d+ - не имя
переменной" ).
Программа анализатора на Рефале будет выглядеть следующим образом:
ANALYZE START ENTRY ArExpr EXTRN prout, card ArExpr = k/pr/ k/арифмвыр/ k/prout/'Введите: '. k/card/...
арифмвыр R V1'+'V2 = k/арифмвыр/V1. k/множитель/V2. E1 = k/множитель/ E1. множитель R V1'*'V2 = k/множитель/V1. k/первичное/V2. V1 = k/первичное/ V1. = '?' – пропущен множитель
первичное '('E1')' = k/арифмвыраж/ E1. S(D)1 E2 = k/число/ S1 E2. S(L)1 E2 = k/имяпеременной/ S1 E2.
Языки логического программированияВ.С. Рублев
34
V1 = '?'V1 – не первичное выражение
число V(D)1 = E1 = '?'E1 – не число
имяпеременной S(L)1 E(LD)2 = E1 = '?'E1 – не имя переменной
pr '?' E1 = к/prout/ 'ошибка: ' E1. E1 = k/prout/ – является выражением.
END
В этой программе вслед за директивой начала START идет директива
ENTRY, определяющая в качестве входа программы функцию ArExpr,
и директива EXTRN, определяющая использование внешних модулей с первичными функциями ввода card и вывода prout.
Функция ArExpr, с которой начинается выполнение программы, выводит функцией prout приглашение "Введите: ", затем вводит функцией card строку выражения, производит анализ выражения функцией арифмвыр и, наконец, выводит функцией pr результат синтаксического анализа.
Описание функций арифмвыр, множитель и первичное соответствует БНФ языка арифметических выражений: их рефал­предложения в точности соответствуют альтернативам в правых частях соответствующих БНФ.
С БНФ буква и цифра языка мы не связываем функций программы, так как в Рефале им соответствуют множество L символов букв и множество D символов цифр. Поэтому функции число и
имяпеременной определены рефал-предложениями с
использованием этих множеств.
В описаниях функций, соответствующих БНФ языка, добавлены в конце рефал-предложения, которые диагностируют соответствующую ошибку и с этой целью помечают диагностику ошибки символом '?', позволяющим функции pr идентифицировать ошибку и вывести
Языки логического программированияВ.С. Рублев
35
диагностику. Второе рефал-предложение функции pr выводит сообщение о верности арифметического выражения, т. е. соответствии с приведенным синтаксисом языка арифметических выражений.
Этот пример показывает, что для построения синтаксического анализатора любого языка надо записать все БНФ языка в виде функций и соответствующих им рефал-предложений. В этом и состоит основное назначение языка Рефал.
Суммирование последовательности чисел
Рассмотрим пример разработки программы суммирования последовательности чисел. Числа вводятся с клавиатуры и суммируются до тех пор, пока не будет введен нуль. В этом случае сумма чисел выдается на экран.
Из постановки задачи следует, что каждое вводимое число должно анализироваться на нулевое значение - признак окончания суммирования и, если оно не нуль, накапливаться в сумме. В качестве начального значения суммы возьмем нуль, а ввод очередного слагаемого будем делать в самой функции суммирования. Таким образом, функция
sum суммирования:
1. должна закончить вычисления, если предыдущее введенное число
- нуль;
2. в противном случае должна добавить к сумме предыдущее введенное число и ввести очередное число.
Эти 2 действия можно осуществить двумя рефал-предложениями функции, что и делает следующая программа.
SUMMA & START & & ENTRY & Summ & EXTRN & prout, card, numb, symb, add Summ = k/pr/ k/sum/(/0/) k/prout/'Добавьте число '.
k/numb/ k/card/....
sum (S(N)1) /0/ = S1 (S(N)1) S(N)2 = k/sum/ (k/add/ (S1) S2.)
Языки логического программированияВ.С. Рублев
36
k/prout/'Добавьте число '. k/numb/ k/card/... pr S(N)1 = к/prout/ 'Сумма=' k/symb/ S1. E1 = k/prout/ 'ошибка: 'E1.
END
Первичная функция numb преобразует вводимое число из цепочки символов в символ-число, add - добавляет в накапливаемую сумму очередное число, а symb - преобразует полученный результат к выводимой цепочке символов.
Упражнения
1. Разработайте программу на Рефале-2 проверки синтаксической правильности условного выражения языка С++, в котором все его части могут содержать условные выражения, идентификаторы переменных и целые числа без знаков между ними других операций.
2. Разработайте программу на Рефале-2 вычисления максимума последовательности целых ненулевых чисел, заканчивающейся нулем.
3. Разработайте программу на Рефале-2 вычисления первого номера максимума последовательности целых ненулевых чисел, заканчивающейся нулем.
4. Разработайте программу на Рефале-2 вычисления произведения последовательности целых ненулевых чисел, ограниченной нулем.
5. Разработайте программу на Рефале-2 вычисления последнего номера минимума последовательности целых ненулевых чисел, заканчивающейся нулем.
Языки логического программированияВ.С. Рублев
37
Язык РЕФАЛ: дополнительные виды памяти
Рекурсивные вычисления и функция порождения процесса: функция apply; пример вычислений на Рефале функции n!. Стековая память Рефала: копилка и вид ее содержимого; функции работы с копилкой; пример сортировки массива. Обменная статическая и динамическая память: ящики и обменные функции; статические и динамические ящики; функции работы с динамическими ящиками; пример программы, работающей с поисковым списком (инициализация списка, добавление элемента списка, поиск элемента по ключу, удаление элемента списка).
Дополнительные виды памяти
Поскольку язык нормальных алгоритмов Маркова является вычислительно полным (можно реализовать любой алгоритм), то и Рефал обладает этим свойством. Однако вышеуказанными средствами Рефала не всегда можно достаточно просто описать любой алгоритм. В случае необходимости (временно запомнить промежуточные результаты, а потом их использовать выборочно для продолжения вычислений) мы можем пока использовать только накопление этих результатов в структурных выражениях, а необходимый при этом постоянный анализ таких выражений ведет к неэффективным алгоритмическим конструкциям. Поэтому с целью повысить эффективность разработки алгоритмов введены к обычным видам памяти ( поле памяти, в котором находятся описание рефал-функций, и поле зрения, в котором находится преобразуемое выражение) дополнительные виды памяти.
Рекурсивные вычисления и функция порождения процесса
Функции Рефала могут быть рекурсивными, т. е. обращаться сами к себе, как мы это видели на примерах предыдущего раздела. Но эти примеры не были связаны с запоминанием в памяти промежуточных вычислений рекурсии. Так, например, при вычислении функции n! происходит рекурсивный спуск от значения аргумента n к значению n-
1 с запоминанием значения n к тому моменту, когда будет вычислено
Языки логического программированияВ.С. Рублев
38
значение (n-1)! и надо будет умножить это значение на запомненное
n. Такой рекурсивный спуск требует, чтобы рефал-машина имела не
одно поле зрения (память) для выполнения процесса вычислений, а много таких полей зрения для порожденных процессов, которые могли бы выполнять вычисления в своей памяти, т. е. в своих полях зрения.
Вызов функции apply имеет другой синтаксис, чем вызов обычной рефал-функции:
<apply /Рефал-функция/ аргументРефал-функции>
Здесь полужирные уголковые скобки являются обязательными синтаксическими единицами в отличие от обычных уголковых скобок, служащих для описания синтаксиса. При вызове apply порождается новое поле зрения и в него помещается терм
k/Рефал-функция/ аргументРефал-функции.
Происходит попытка вычисления этого терма, и результат этого вычисления возвращается в то поле зрения, из которого была вызвана функция apply.
Поле того как результат замены сформирован, дополнительное поле зрения, созданное в результате обращения к apply, уничтожается. При рекурсивном обращении к apply образуется стек полей зрения.
В качестве примера мы рассмотрим создание функции вычисления факториала n!.
FACT START ENTRY Factorial EXTRN prout, card, numb, symb, mul, first, apply Factorial = k/fact/ k/numb/ k/prout/'n: '. k/card/...
fact /0/ = k/pr/ '!=1' E1 = k/pr/ k/symb/E1.'!=' k/symb/ k/f0/ E1...
f0 E1 = <apply /f/ E1>
f /1/ = /1/
Языки логического программированияВ.С. Рублев
39
E1 = k/mul/ (E1) k/f0/ k/m1/ E1...
pr = E1 = k/pr1/ k/first/ /80/ E1..
pr1 (E1)E2 = k/prout/ E1. k/pr/ E2. '*'E1 = k/prout/ E1.
END
В этой программе функция Factorial вводит цепочку цифр данного, преобразует к символу-числу и вызывает функцию fact, которая вычисляет значение факториала от аргумента и готовит полученное значение к выводу на экран.
Функция fact для вычислений вызывает функцию f0, которая при помощи функции apply порождает процесс вычисления функции f. Эта функция накапливает произведение аргумента на значение факториала от аргумента, уменьшенного на 1, для чего снова вызывает функцию f0 с этим уменьшенным значением аргумента. Функция f0 снова порождает процесс вычисления f с новым значением аргумента и так делается до тех пор, пока значение аргумента не станет равно 1.
В этом случае будет происходить завершение вычисления функции f в обратном порядке вызванных процессов. Память завершающихся процессов будет удаляться, а значение, вычисленное в каждом процессе, будет передаваться в вызвавший процесс до тех пор, пока не закончится вычисление функции f0 в первом вызванном процессе.
Функция fact вызовет после вычислений преобразование результата к цепочке символов и вызов функции pr для вывода на экран результата.
Здесь следует обратить внимание на то, что полученная цепочка символов может не уместиться в одной строке экрана. Поэтому функция
pr отщепляет от цепочки 80 символов при помощи функции first и
передает на печать функцию pr1, которая печатает отщепленную часть и возвращает остаток функции pr для следующего отщепления. Так происходит до тех пор, пока вся цепочка символов не будет выведена.
Языки логического программированияВ.С. Рублев
40
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]