Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Языки логического программирования. Учебное пособие
.pdf
Стековая память Рефала
Рефал позволяет использовать память, называемую копилкой . Она
предоставляет возможность организовать любое количество стеков и
запоминать информацию из поля зрения в том или ином стеке, а также
извлекать эту информацию из стека в поле зрения.
Содержимое копилки перед началом работы рефал-программы всегда
пусто, а в процессе работы рефал-программы имеет следующий вид:
('<ИмяСтека1>=<Выраж1>'). . . ('<ИмяСтекаN>=<ВыражN>'),
где <ИмяСтека1> ... <ИмяСтекаN> - произвольные имена
стеков (необязательно различные), а выражения <Выраж1>...
<ВыражN> - произвольные рефал-выражения, записанные в эти стеки.
При записи в стек записываемое выражение добавляется вместе с
именем стека в копилку слева.
Первичная функция br1) '<ИмяСтека>=<Выраж>' добавляет в
копилку слева выражение аргумента и возвращает пустое выражение.
Например, в результате выполнения
k/br/ 'X=A'. k/br/ 'X=B'.
в копилку слева добавится выражение ('X=B')('X=A'), т. е.
образуется стек с именем X (если его ранее не было в копилке ) и в него
добавится сначала символ 'A', а затем - символ 'B'.
Первичная функция dg2) '<ИмяСтека>' ищет в копилке слева
структурное выражение ('<ИмяСтека>=<Выраж>') с именем стека
<ИмяСтека>, удаляет его из копилки (т. е. из стека с этим именем) и
возвращает выражение <Выраж> ; в поле зрения. Если в копилке не
найдется имени стека, то будет возвращено пустое выражение.
Например, пусть копилка имеет вид:
('X=A')('Y=B')('Y=C').
Тогда после выполнения
Языки логического программированияВ.С. Рублев
41

k/dg/ 'Y'.
копилка примет вид:
('X=A')('Y=C'),
а функция возвратит значение 'B'.
Первичная функция cp3) <ИмяСтека> возвращает то же, что и функция
dg. но не удаляет ничего из копилки ( копилка не изменяется).
Например, после выполнения
k/dg/ 'Y'.
для результатов предыдущего примера копилка не изменится, а функция
возвратит значение 'C'.
Первичная функция rp4) '<ИмяСтека>=<Выраж>', так же как и
функция br, добавляет в копилку слева структурное выражение с
аргументом, но удаляет перед этим последнюю (самую левую) запись с
именем стека в копилке, если она там была. Возвращает пустое
выражение. Например, пусть копилка имеет вид:
('X=A')('Y=B')('Y=C').
Тогда после выполнения
k/rp/ 'Y=C'.
копилка примет вид: ('X=A')('Y=C')('Y=C').
Первичная функция dgall5) возвращает в поле зрения все выражение
копилки и очищает копилку полностью. Например, пусть копилка имеет
вид:
('X=A')('Y=B')('Y=C').
Тогда после выполнения
k/dgall/ .
Языки логического программированияВ.С. Рублев
42

в поле зрения вернется ее выражение ('X=A')('Y=B')('Y=C') и
копилка очистится.
В качестве примера мы рассмотрим сортировку массива
положительных целых чисел методом вставки. Используются 2 стека: Y,
в котором формируется отсортированная в возрастающем порядке
последовательность, и X, который является вспомогательным: в нем
содержится начало уже отсортированной последовательности в
обратном порядке.
Каждое вводимое число z сравнивается с числом, находящимся в
вершине стека Y, и если оно меньше, то идет поиск его места в стеке X,
а если оно не меньше, то числа из Y переписываются в X до тех пор,
пока z не станет меньше, чем в вершине Y (или Y не станет пустым),
после чего оно записывается в Y.
При поиске места z в X оно сравнивается с числом, находящимся в
вершине X, и если оно меньше, то числа из X переписываются в Y до
тех пор, пока z не станет не меньше, чем в вершине X - тогда оно
записывается в X.
При вводе нуля (признак окончания ввода) остаток в X переписывается
в Y, а затем извлекается из стека Y для вывода через пробел на экран.
SORT START
ENTRY Sort
EXTRN prout, card, numb, symb, sub, first, br, dg, cp
* ввод 1-го числа в стек Y
Sort = k/n1/ k/br/'Y=' k/numb/
k/prout/'Последовательность чисел: '. k/card/....
* ввод очередного числа z
n1 E1 = k/n2/ k/br/'Z=' k/numb/ k/card/... k/cp/'Z'..
* z=0? X–>Y : z–y
n2 /0/ = k/t1/ k/cp/'X'..
S(N)1 = k/n3/ k/sub/ (S1) k/cp/'Y'...
Языки логического программированияВ.С. Рублев
43

* z<y?
n3 '–'E1 = k/nx/ k/cp/'X'..
E1 = k/ny/ k/cp/'Y'..
* X=0? z->X : z–x
nx = k/n1/ k/br/'X=' k/dg/'Z'...
S(N)1 = k/n4/ k/sub/ (k/cp/'Z'.) S1..
* z<x? X->Y : Z->X
n4 '–'E1 = k/br/'Y=' k/dg/'X'.. k/nx/ k/cp/'X'..
E1 = k/n1/ k/br/'X' k/dg'Z'...
* Y=0? Z->Y : z–y
ny = k/n1/ k/br/'Y=' k/dg/'Z'...
S(N)1 = k/n5/ k/sub/ (k/cp/'Z'.) S1..
* z<y? Z->Y : Y->X
n5 '–'E1 = k/br/'Y=' k/dg/'Z'.. k/n1/ k/cp/'X'..
E1 = k/br/'X' k/dg'Y'.. k/ny/ k/cp/'Y'..
* X=0? –>вывод Y : X–>Y
t1 = k/m2/ () k/cp/'Y'..
E1 = k/t1/ k/br/'Y' k/dg/'X'..k/cp/'X'..
* Y=0? –>печать (E) : –>вывод Y
m2 (E1) = k/pr/ E1.
(E1)S(N)2 = k/m2/ (E1' 'k/symb/S2.) k/dg/'Y'..
* выделение 80 символов для печати
pr =
E1 = k/pr1/ k/first/ /80/ E1..
* печать выделенного или остатка
pr1 (E1)E2 = k/prout/ E1. k/pr/ E2.
'*' E1 = k/prout/ E1.
END
Языки логического программированияВ.С. Рублев
44

Обменная статическая и динамическая память Рефала
Рефал-выражения являются способом представления древовидных
структур, которые в основном и используются при синтаксическом
анализе конструкций какого-либо языка. Но в некоторых случаях
необходимо представлять структуры, которые не обязательно являются
древовидными. Таковы, например, сложные динамические структуры,
основанные на цепных списках: очереди, деки, поисковые списки,
графы, разреженные матрицы. И хотя любые структуры можно
представлять в виде набора некоторого количества деревьев, это не
всегда удобно.
Средством Рефала, дающим возможность обрабатывать произвольные
динамические структуры, является обменная память , называемая также
ящиками. Каждый ящик содержит произвольное рефал-выражение (
содержимое ящика ), которое может изменяться в процессе работы
программы. Каждому ящику однозначно соответствует своя обменная
функция - функция, с помощью которой можно получить доступ к
содержимому ящика. Имя обменной функции совпадает с именем
ящика.
Работа обменной функции осуществляется термом
<имя обменной функции>/ <рефал-выражение>.
В результате вызова такой функции содержимое ящика помещается в
поле зрения, а в ящик записывается указанное рефал-выражение
(происходит обмен информацией между полем зрения и ящиком ).
Все ящики делятся на статические и динамические. Статические ящики
описываются директивой SWAP:
<пробел> SWAP <пробел> <имя ящика 1>, ... ,<имя ящика N>
и не могут уничтожаться во время работы. Например, в следующем
фрагменте программы
SWAP X,Y
swxy = k/X/'A'. k/Y/ 'B'. k/sw/ /X/ /Y/.
sw Sx Sy = k Sx k Sy k Sx...
Языки логического программированияВ.С. Рублев
45

при вызове функции
k/swxy/.
в ящики X и Y сначала будут занесены символы 'A' и 'B' соответственно,
а затем в результате выполнения терма
k/sw/ /X/ /Y/.
содержимое ящиков поменяется местами.
Динамические ящики порождаются в процессе работы программы
первичной функцией new. В результате выполнения терма
k/new/ <рефал-выражение>.
создается новый ящик, в него помещается рефал-выражение аргумента
и возвращается символ-ссылка, которая является именем новой
обменной функции. Символ-ссылка играет роль указателя, т. е. имеет
значение адреса памяти, и ее нельзя употреблять в программе в виде
констант (но можно напечатать при отладке программы). Например,
описанный выше пример со статическими ящиками мы могли бы
заменить на аналогичный с динамическими ящиками:
EXTRN new
swrr = k/sw/ k/new/'A'/. k/new/ 'B'..
sw Sx Sy = k Sx k Sy k Sx...
Теперь при вызове функции swrr будут выполнены 2 вызова функции
new, в результате которых образуются 2 ящика, например, с именами
/%0321514/ и /%0674236/. В эти ящики будут записаны символы
'A' и 'B' соответственно, а затем при вызове функции sw их содержимое
поменяется местами.
Обращаться напрямую к динамическому ящику мы не можем, но можем
запомнить символ-ссылку и вызывать обменную функцию для нее.
Если динамической памяти не хватит, то система запустит программу
сборки мусора. Эта программа исследует, есть ли ссылки на тот или
иной динамический ящик в поле зрения или других статических и
динамических ящиках. Если такой ссылки нет, то соответствующий
Языки логического программированияВ.С. Рублев
46

ящик удаляется (память освобождается). Это снимает с программиста
заботу о чистке памяти.
Имеются 5 первичных функций, которые удобнее по сравнению с
прямым использованием обменной функции.
Функция gtr (взять по ссылке)
k/gtr/ <ссылка>. ,
где ссылка - это имя статического (симовол-метка) или динамического
(символ-ссылка) ящика, извлекает и возвращает содержимое ящика.
Функция rdr (прочитать по ссылке)
k/rdr/ <ссылка>.
копирует в поле зрения содержимое ящика.
Функция ptr (положить по ссылке)
k/ptr/ <ссылка> <рефал-выражение>.
добавляет в ящик справа рефал-выражение аргумента и возвращает
пустое выражение.
Функция wtr (записать по ссылке)
k/wtr/ <ссылка> <рефал-выражение>.
заменяет в ящике содержимое на рефал-выражение аргумента и
возвращает пустое выражение.
Функция swr (Обменять по ссылке)
k/swr/ <ссылка> <рефал-выражение>.
делает обмен между содержимым ящика и полем зрения (в поле зрения
выдается содержимое ящика, а в ящик записывается рефал-выражение
аргумента).
Языки логического программированияВ.С. Рублев
47

Рассмотрим пример работы с поисковым списком. Элементы списка
будут располагаться в динамических ящиках. Структура каждого
элемента:
<ссылка следующего ящика> (<ключ>) <данные>
Последний элемент в цепочке динамических ящиков имеет пустую
ссылку следующего ящика, а на первый элемент ссылается статический
ящик, который определяет голову списка. Функции Init, In, Find,
Out производят
1. инициализацию списка;
2. ввод (добавление) элемента;
3. поиск по ключу данных (возвращает ссылку на ящик, если найдет,
или пустое выражение, если не найдет; кроме того, возвращает в
статическом ящике pred ссылку на предыдущий ящик в цепочке
списка);
4. удаление элемента (по ключу).
Следующая программа выводит меню действий:
0. Завершить программу.
1. Инициализировать список.
2. Добавить элемент списка.
3. Найти данные по ключу.
4. Удалить элемент по ключу.
Введите пункт меню.
и осуществляет эти действия.
LIST START
ENTRY Init, In, Find, Out, List
EXTRN prout, card, numb, symb,
* ключ, данные, указатели головы списка и
* элементов текущего, предыдущего и след.
SWAP key, data, pl, cur, pred, next
* начальный вывод меню и выбор действия
List = k/menu/. k/act/ k/pnkt/..
Языки логического программированияВ.С. Рублев
48

* вывод меню
menu = k/prout/ '0. Завершить программу.'.
k/prout/ '1. Инициализировать список.'.
k/prout/ '2. Добавить элемент списка.'.
k/prout/ '3. Найти данные по ключу.'.
k/prout/ '4. Удалить элемент по ключу.'.
k/prout/ ' Выберите пункт меню.'.
* выбор пункта меню
pnkt = k/numb/ k/card/..
* выбор действия
act =
/0/ =
/1/ = k/Init/. k/menu/. k/act/ k/pnkt/..
/2/ = k/In/. k/menu/. k/act/ k/pnkt/..
/3/ = k/inkey/. k/Find/. k/prd/. k/menu/. k/act/ k/pnkt/..
/4/ = k/inkey/. k/Out/. k/prd/. k/menu/. k/act/ k/pnkt/..
E1 =
* ввод ключа
inkey E1 = k/key/ k/numb/ k/prout/'Ключ: '. k/card/...
* ввод данных
indat E1 = k/data/ k/prout/'данные: '. k/card/..
* инициализация списка
Init E1 = k/pl/.
* добавление элемента
In E1 = k/pl/ k/new/ k/gtr/ /pl/. (k/rdr/ /key/ k/inkey/..)
k/rdr/ /data/ k/indat/....
* поиск по ключу: опред. тек.эл. cur
Find = k/wtr/ /pred/. k/wtr/ /cur/ k/rdr/ /pl/..
k/f1/ k/rdr/ /cur/..
* нет текущего? не найден : анализ текущего
Языки логического программированияВ.С. Рублев
49

f1 = k/data/ 'Не найден'.
S(R)c = k/f2/ k/rdr/ k/rdr/ /cur/...
* анализ текущего
f2 S(R)n(S(N)k)Ed = k/wrt/ /next/ Sn. k/data/ Ed. k/f3/ Sk.
* сравнение ключей k–key
f3 S(N)k = k/f4/ k/sub/ (Sk) k/rdr/ /key/...
* k=key? найден : следующий
f4 /0/ =
E1 = k/pred/ k/cur/ k/next/... k/f1/ k/rdr/ /cur/..
* вывод результата из данных
prd E1 = k/prout/ k/rdr/ /data/..
* удаление: есть ли ключ?
Out E1 = k/Find/. k/o1/ k/rdr/ /cur/..
* удаление: есть ли предыдущий?
o1 = k/data/ 'Не найден'.
Vc = k/o2/ k/rdr/ /pred/..
* удаление: есть ли предыдущий?
o2 = k/pl/ k/rdr/ /next/.. k/data/ 'Удален'.
Vp = k/o3/ k/rdr/ k/rdr/ /pred/...
* удаление: изменение предыдущего
o3 S(R)cEd = k/wtr/ /pred/ k/rdr/ /next/. Ed. k/data/ 'Удален'.
END
Отметим, что в случае, когда в статическом ящике A находится ссылка на
другой динамический ящик %B, к которой мы непосредственно не
можем обратиться, то для получения данных последнего ящика нужно
дважды вызвать данные:
k/rdr/ k/rdr/ /A/..
Языки логического программированияВ.С. Рублев
50
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
