Рефал — предметно-эквациональный способ определения функций на свободноммоноиде с дополнительнойодноместной операцией .
(А. П. Бельтюков. Определение
Рефал —замечательный язык!На нем можно запрограммировать все, что угодно, если, конечно, других языков не знать.
Студент УдГУ
действия глобальны, условия глобальны. (5.1)
В этом случае необходимы мощные средства распознавания глобальных условий, которые естественно погрузить в модель вычислений языка как атомарные средства. Программист при формулировке условий может позволить себе не задумываться о том, как происходит их распознавание, и сосредоточиться на самой важной части: описании перестроек структуры.
В имеющихся сейчас двух наиболее развитых и альтернативных системах
подготовка информации для действий в ходе распознавания условий, (5.2)
пожалуй, является характерной для
В настоящее время глобальная проверка условий в системах 1+а -- выражение, 1+X -- X должно быть подставлено а.
Такой способ работы согласуется с формулировкой глобальных условий в логике и в теории алгоритмов, но его нельзя считать a priori исчерпывающим. В дальнейшем могут появиться другие формы глобальной проверки данных.
Язык
В основе языка лежит (другой по сравнению с языком PROLOG) частный случай операции
Язык
Данные, обрабатываемые языком {a+[b-(c+d)**2]/3}*(a+d). В
Выражения языка
E, заключенное в E — выражение, A — описанный в программе атом, AE, заключенное в A называется детерминативом этого выражения.В конкретном синтаксисе < >, структурные -- ( ). Атомы делятся на:
В
Дадим точные определения.
Объектное выражение — выражение, не содержащее функциональных скобок.
Минимальное функциональное выражение — выражение, имеющее вид <E>, где E — объектное выражение.
Детерминатив — первый символ в функциональной скобке.
Детерминатив интерпретируется как имя функции, обрабатывающей содержимое функциональных скобок. Эта функция должна определяться статически, поэтому в ходе вычислений не могут образовываться выражения вида < <e.1> e.2>. В подавляющем большинстве случаев детерминатив должен быть именем, но некоторые обычные символы, например +, также могут использоваться в качестве детерминативов.
Пример 5.2.3. Рассмотрим пример памяти
'aaxzACDE' <Sort < Perm 'G'1.5E5 > <Perm 115 'F'>
<Perm 112 -2.0E-5>><Sort AllRight Sort Perm 'QRTS'>'XZ<(')
'aaxzACDE' и 'XZ<('). У символов, представляющих скобки, есть 'обычные' двойники, не обязательно имеющие парные и не влияющие на структурирование выражения. Первый атом Perm стоит в позиции функции, а последний из атомов Perm стоит в позиции данных, так что имена функций могут 123 — это один атом, '123' — три атома, -123.0 — опять один атом, -123 — два атома: символ '-', после которого стоит число.
Кроме основной части
Stack1 '=' 15 Stack2 '=' Perm B A
21 Perm C2 C1
-45 Perm 'X' 20
60
В принципе, несколько стеков — избыточная конструкция. Но, поскольку здесь стеки рассматриваются как общие области памяти, лучше в каждом модуле иметь свой стек. К сожалению, явной связи между стеками и модулями в
Допустим, есть строка . Однако он не является обязательным.
(рис 5.1) Реализация структуры данных Рефала
При реализации языка
Более того, описанные Турчиным алгоритм и структуры данных являются отличной базой для точного определения семантики на базе
Преобразование выражений в формат для обработки делается один раз, при вводе информации либо при трансляции программы.
Рассмотрим конкретный синтаксис выражений.
Идентификатор — любая последовательность цифр и букв, начинающаяся с большой буквы. Идентификатор является символьным литералом и представляет составной символ. Символы, заключенные в одинарные ' ' кавычки, являются символьными литералами, представляют сами себя. Дважды повторенная кавычка представляет кавычку. Двойные кавычки " " используются для ограничения имени составного символа, не обязательно являющегося
В конкретном синтаксисе обозначение переменной включает тип и символ переменной, записываемые как тип.атом. В стандартном s.First ), термовые ( t.Inner ) и общие ( e.Last ).
Значением символьной переменной служит атом, термовой — терм (символ или выражение в скобках), общей — произвольное (может быть, пустое) объектное выражение.
Основные два шага в
Me в объектное выражение E называется такая Me, что после применения данной Me совпадет с E
Заметим, что одно и то же
e.Begin s.Middle e.End (5.3)
и объектное выражение
AhAhAh 'OhOhOh' (Ugu','Udgu) '(((' Basta'!'
(5.4)
Имеется 11 вариантов
Если у Me есть много вариантов E, то они упорядочиваются по предпочтительности в следующем порядке.
Пусть Env1 и Env2 — два варианта Me в P. Рассмотрим все вхождения переменных в Me. Если Env1 и Env2 не совпадают, они приписывают некоторым переменным различные значения. Найдем в P самое первое слева вхождение переменной, которому Env1 и Env2 приписывают разные значения и сравним длину этих значений. Та из
Например, сопоставим объектное выражение (A1 A2 A3)(B1 B2) с образцом e.1 (e.X s.A e.Y) e.2. В результате получится следующее множество вариантов сопоставления:
{e.1 = , eX = , sA = A1, eY = A2 A3, e.2 = (B1 B2) }
{e.1 = , eX = A1, sA = A2, eY = A3, e.2 = (B1 B2) }
{e.1 = , eX = A1 A2, sA = A3, eY = , e.2 = (B1 B2) }
{e.1 = (A1 A2 A3), eX = , sA = B1, eY = B2, e.2 = }
{e.1 = (A1 A2 A3), eX = B1, sA = B2, eY = , e.2 = }
Варианты сопоставления перечислены в соответствии с их приоритетами, т. е. самый первый вариант находится на первом месте и т. д. Описанный способ упорядочения вариантов сопоставления называется сопоставлением слева направо.
Тот алгоритм
Вхождения атомов, скобок и переменных будут называться элементами выражений. Пропуски между элементами будут называться узлами. Сопоставление E: P определяется как процесс отображения, или проектирования, элементов и узлов образца P на элементы и узлы объектного выражения E. Графическое представление успешного сопоставления приведено на рис. 5.2. Здесь узлы представлены знаками o.
Следующие требования являются инвариантом алгоритма сопоставления и их выполнение обеспечивается на каждой его стадии.
N2 расположен в P правее узла N1, то проекция N2 в E может либо совпадать с проекцией N1, либо располагаться справа от нее (линии проектирования не могут пересекаться).
(рис 5.2) Сопоставление E : P является отображением P на E. Здесь объектным выражением E является 'A'((2'B'))'B', а образцом P является 'A'(e.1 t.2)s.3Предполагается, что в начале сопоставления граничные узлы P отображаются в граничные узлы E. Процесс отображения описывается при помощи следующих шести правил. На каждом шаге отображения правила 1–4 определяют следующий элемент, подлежащий отображению; таким образом, каждый элемент из P получает при отображении уникальный номер.
На рис. 5.2 сопоставление производится следующим образом. Вначале имеется два жестких элемента с одним отображенным концом: 'A' и s.3. В соответствии с Правилом 3 отображается 'A', и этот элемент получает при отображении номер 1.Номера 2 и 3 будут назначены левой и правой скобкам согласно Правилам 3 и 1. Внутри скобок начинается перемещение справа налево, так как t.2 является жестким элементом, который может быть отображен, в то время как значение e.1 еще не может быть определено. На следующем шаге обнаруживается, что e.1 является закрытой переменной, чью проекцию не требуется обозревать для того, чтобы присвоить ей значение; что бы ни было между двумя узлами, это годится для присвоения (на самом деле, значение e.1 оказывается пустым). Отображение s.3 завершает сопоставление. Расположение отображающих номеров над элементами образца дает наглядное представление описанного алгоритма:
1 2 5 4 3 6 'A' ( e.1 t.2 ) s.3
Этот сложный алгоритм упрятан в простые программные конструкции.
Программа на
Имя функции {Последовательность сопоставлений}
Каждое сопоставление имеет вид
Образец = Метавыражение;
Относительное расположение членов последовательности определений никакой роли не играет, и функции можно группировать из логических или технологических соображений. Относительное расположение сопоставлений внутри определения функции существенно.
Пример 5.3.2. Рассмотрим пример
Pre_alph {
*1. Отношение рефлексивно
s.1 s.1 = T;
*2. Если буквы различны, проверить, входит ли
* первая из них в алфавит до второй
s.1 s.2 = <Before s.1 s.2 In <Alphabet>>; }
Before {
s.1 s.2 In e.A s.1 e.B s.2 e.C = T;
e.Z = F; }
Alphabet {
= 'abcdefghijklmnopqrstuvwxyz'; }
Строки, начинающиеся с *, служат комментариями. Последняя из функций введена для технологичности, чтобы определение алфавита было в одном месте и его легко было изменять. Так что функция с пустым образцом может пониматься как константное выражение. In является атомом-разделителем, заведомо не встречающимся в алфавите.
Последнее из правил сопоставления в Before применимо всегда. Такое сопоставление гарантирует, что предикат никогда не заканчивается неудачей.
Если взаимное расположение функций никакой роли не играет, то внутри функции расположение сопоставлений важно. Сначала применяется первое из сопоставлений, при неудаче переходят ко второму и так далее до последнего.
Заметим, что в языке e.A=Обработка ошибки ;
Имеются также встроенные функции, в частности, функции работы с числами <'+' s.Number1 s.Number2> и подобные ей.
Рассмотрим связь между языком и программным окружением.
Для запуска
<Go >
Таким образом, вначале применяется функция Go к пустому Go выглядит примерно следующим образом:
$ENTRY Go { =<Prog <Open 'r' 1 'data.txt'><Get 1> >}
В нашей программе 5.3.1 алфавит определен статически. Было бы хорошо иметь возможность заменять эту глобальную информацию. Для хранения динамической глобальной информации (чаще всего числовых характеристик либо словарей) в языке
<Br e.N '=' e.Е >
рассматривает свой первый аргумент (который должен быть строкой символов, не включающей '=' ) как имя стека, и помещает свой второй аргумент на вершину этого стека. Если стек был пуст, то он создается. Соответственно, функция
<Dg e.N>
выкапывает верхушку стека. Если стек пуст, то ошибки нет, просто выдается пустое выражение. Несколько других функций дополняют возможности работы с глобальной информацией. Cp копирует верхушку стека без ее удаления, замещает верхушку стека на свой аргумент, DgAll выкапывает сразу весь стек.
Ввод-вывод организован в 'r' и присваивает ему номер. Одна строка символов из файла читается с помощью функции Get, заменяющей свой вызов на прочитанную строку, одна строка пишется в файл путем функций
<Put s.Channel e.Expression>
либо
<Putout s.Channel e.Expression>
Вторая функция стирает свое
Следует заметить, что эти функции читают и пишут именно последовательности символов. При их использовании программист должен сам преобразовать последовательности цифр в числа, а скобки-символы в
<Input s.Channel> или <Input e.File-name>
<Xxin e.File-name> или <Xxin s.Channel>
<Xxout s.Channel e.Expr> или
<Xxout (e.File-name)e.Expr>
Первая из функций предназначена для ввода файлов, подготовленных вручную. Вторая и третья — для обмена промежуточной информацией с диском.
Только что перечисленные функции вместе с функцией Go требуют объяснения инструментов модульности в Go. Функции, предоставляемые в пользование другим модулям, описываются спецификатором $ENTRY как входы. В свою очередь, использующий модуль должен описать
$EXTRN F1,F2,F3;
Вызов программы, состоящей из нескольких модулей, производится оператором примерно следующего вида:
refgo prog1+functions+reflib
Модуль основной программы должен идти первым. Никаких средств включить требование вызова модуля в текст другого модуля нет, модули сопрягаются внешним образом. При конфликтах имен берется определение функции из первого в порядке
В частности, только что описанные
Важнейшими средствами современного Mu, которая заключает свой аргумент в Mu работает так, как работало бы определение
Mu { s.F e.X = <s.F e.X> },
если бы оно было синтаксически допустимо.
В частности, через Mu работает стандартный модуль Upd, которая должна быть добавлена к модулю, где осуществляется динамическое вычисление выражений. Например, если добавить описание
$ENTRY Upd {e.X = <Mu e.X>;}
то командная строка refgo e+prog1 приведет к требованию написать выражение. Это выражение будет сделано prog1 и вычислено, а результат выведен. Например, написав для программы 5.3.1
<Alphabet>
мы получим в качестве результата
'abcdefghijklmnopqrstuvwxyz'
Естественно возникает вопрос об обработке внутри языка не только объектных, но и произвольных выражений. Для этого имеются стандартные функции Up и . Первая из них превращает объектное выражение в выражение произвольного вида, вторая кодирует свою область зрения (ею, по семантике языка, может быть лишь объектное выражение) в форме, годящейся для общих выражений и даже
При решении сложных задач на )(. Открывающую скобку высшего уровня представляем как ((, место, куда ведет ссылка — как ))(, закрывающую скобку высшего уровня как )).
И в заключение рассмотрим достаточно сложный алгоритм на
Пусть у нас дано выражение с различными парными скобками (в конкретном случае мы используем пары '()[]{}<>', но программа составлена так, чтобы эти пары можно было заменить в любой момент). Для эффективной работы на
При записи данного алгоритма используется еще одна возможность
Иерархически вложенные функции и условия в принципе не нужны для
$ENTRY Go{=<Init>;}
$EXTRN Xxout;
* Инициализация поля зрения и констант
Init{=<Open 'r' 1 'Input.txt'><Trans <Acquire(<Get 1>)";}
Acquire {
e.Got ()= e.Got;
* Конец ввода - пустая строка
e.Got (e.New)=<Acquire e.Got e.New (<Get 1>)>;
}
Brackets {=('()')('[]')('')('<>');}
Trans {
e.A =<Result <Pairing () e.A > >;
}
Pairing {
* В первой скобке содержится последовательность всех незакрытых
* скобок вместе с сегментами данных, подлежащими помещению
* в даннуюпару скобок;
* каждый сегмент данных также заключен в скобки
(e.Unclosed (e.LastUn)(s.Lbrack e.Middle)) s.Rbrack e.Last,
<Brackets>: e.A(s.Lbrack s.Rbrack ) e.B =
* Встретилась правая скобка, парная последней незакрытой;
* выбрасываем отработанный сегмент из поля зрения
<Pairing (e.Unclosed (e.LastUn (s.Lbrack e.Middle s.Rbrack))) e.Last>;
((s.Lbrack e.Middle)) s.Rbrack e.Last,
<Brackets>: e.A(s.Lbrack s.Rbrack ) e.B =
* Парная незакрытой, находящейся внутри другой незакрытой
(s.Lbrack e.Middle s.Rbrack) <Pairing () e.Last>;
(e.Unclosed (e.LastUn)(s.Lbrack e.Middle)) s.Rbrack e.Last,
<Brackets>: e.A(s.Lbrack1 s.Rbrack ) e.B =
* Непарные скобки
<Prout "Brackets Mismatch!"> Error;
(e.Unclosed ) s.Lbrack1 e.Last,
<Brackets>: e.A(s.Lbrack1 s.Rbrack ) e.B =
* Еще одна открывающая скобка; создаем новую группу данных
<Pairing (e.Unclosed (s.Lbrack1)) e.Last>;
() s.Lbrack e.Last,
<Brackets>: e.A(s.Lbrack s.Rbrack ) e.B =
* Первая открывающая скобка
<Pairing ((s.Lbrack)) e.Last>;
() s.Rbrack e.Last,
<Brackets>: e.A(s.Lbrack s.Rbrack ) e.B =
* Первая скобка - закрывающая
<Prout "Extra right bracket"> Error;
* Нейтральный символ вне скобок
() s.Other e.Last = s.Other <Pairing () e.Last>;
* Выражение и скобки исчерпаны - успех
() =;
* Выражение исчерпано, а скобки - нет
(e.Unclosed (e.Lastun))=<Prout "Not all brackets are closed" Error;
* Нейтральный символ в очередной скобке
(e.Unclosed (e.Lastun))s.Other e.Last=
<Pairing (e.Unclosed(e.Lastun s.Other)) e.Last>;
}
Result {
* Если была ошибка, выйти
e.A Error=;
* Иначе вывести результат для дальнейшего использования
e.A =<Xxout ('output.rdt') e.A>;
}
Для того, чтобы нагляднее увидеть влияние стиля на программные решения, сравните эту программу с развитием программы в традиционном стиле, приведенным в § 11.5 книги . Данная программа намного выразительнее, короче, легче модифицируема и не менее эффективна, чем программа 11.5.3 из указанного параграфа.
В практике работы со студентами автору приходилось сталкиваться с ситуацией, когда программа для одной и той же задачи, написанная на традиционном языке, была более чем в пять раз длиннее программы на
Из литературы по языку
Рефал — предметно-эквациональный способ определения функций на свободноммоноиде с дополнительнойодноместной операцией .
(А. П. Бельтюков. Определение
Рефал —замечательный язык!На нем можно запрограммировать все, что угодно, если, конечно, других языков не знать.
Студент УдГУ
действия глобальны, условия глобальны. (5.1)
В этом случае необходимы мощные средства распознавания глобальных условий, которые естественно погрузить в модель вычислений языка как атомарные средства. Программист при формулировке условий может позволить себе не задумываться о том, как происходит их распознавание, и сосредоточиться на самой важной части: описании перестроек структуры.
В имеющихся сейчас двух наиболее развитых и альтернативных системах
подготовка информации для действий в ходе распознавания условий, (5.2)
пожалуй, является характерной для
В настоящее время глобальная проверка условий в системах 1+а -- выражение, 1+X -- X должно быть подставлено а.
Такой способ работы согласуется с формулировкой глобальных условий в логике и в теории алгоритмов, но его нельзя считать a priori исчерпывающим. В дальнейшем могут появиться другие формы глобальной проверки данных.
Язык
В основе языка лежит (другой по сравнению с языком PROLOG) частный случай операции
Язык
Данные, обрабатываемые языком {a+[b-(c+d)**2]/3}*(a+d). В
Выражения языка
E, заключенное в E — выражение, A — описанный в программе атом, AE, заключенное в A называется детерминативом этого выражения.В конкретном синтаксисе < >, структурные -- ( ). Атомы делятся на:
В
Дадим точные определения.
Объектное выражение — выражение, не содержащее функциональных скобок.
Минимальное функциональное выражение — выражение, имеющее вид <E>, где E — объектное выражение.
Детерминатив — первый символ в функциональной скобке.
Детерминатив интерпретируется как имя функции, обрабатывающей содержимое функциональных скобок. Эта функция должна определяться статически, поэтому в ходе вычислений не могут образовываться выражения вида < <e.1> e.2>. В подавляющем большинстве случаев детерминатив должен быть именем, но некоторые обычные символы, например +, также могут использоваться в качестве детерминативов.
Пример 5.2.3. Рассмотрим пример памяти
'aaxzACDE' <Sort < Perm 'G'1.5E5 > <Perm 115 'F'>
<Perm 112 -2.0E-5>><Sort AllRight Sort Perm 'QRTS'>'XZ<(')
'aaxzACDE' и 'XZ<('). У символов, представляющих скобки, есть 'обычные' двойники, не обязательно имеющие парные и не влияющие на структурирование выражения. Первый атом Perm стоит в позиции функции, а последний из атомов Perm стоит в позиции данных, так что имена функций могут 123 — это один атом, '123' — три атома, -123.0 — опять один атом, -123 — два атома: символ '-', после которого стоит число.
Кроме основной части
Stack1 '=' 15 Stack2 '=' Perm B A
21 Perm C2 C1
-45 Perm 'X' 20
60
В принципе, несколько стеков — избыточная конструкция. Но, поскольку здесь стеки рассматриваются как общие области памяти, лучше в каждом модуле иметь свой стек. К сожалению, явной связи между стеками и модулями в
Допустим, есть строка . Однако он не является обязательным.
(рис 5.1) Реализация структуры данных Рефала
При реализации языка
Более того, описанные Турчиным алгоритм и структуры данных являются отличной базой для точного определения семантики на базе
Преобразование выражений в формат для обработки делается один раз, при вводе информации либо при трансляции программы.
Рассмотрим конкретный синтаксис выражений.
Идентификатор — любая последовательность цифр и букв, начинающаяся с большой буквы. Идентификатор является символьным литералом и представляет составной символ. Символы, заключенные в одинарные ' ' кавычки, являются символьными литералами, представляют сами себя. Дважды повторенная кавычка представляет кавычку. Двойные кавычки " " используются для ограничения имени составного символа, не обязательно являющегося
В конкретном синтаксисе обозначение переменной включает тип и символ переменной, записываемые как тип.атом. В стандартном s.First ), термовые ( t.Inner ) и общие ( e.Last ).
Значением символьной переменной служит атом, термовой — терм (символ или выражение в скобках), общей — произвольное (может быть, пустое) объектное выражение.
Основные два шага в
Me в объектное выражение E называется такая Me, что после применения данной Me совпадет с E
Заметим, что одно и то же
e.Begin s.Middle e.End (5.3)
и объектное выражение
AhAhAh 'OhOhOh' (Ugu','Udgu) '(((' Basta'!'
(5.4)
Имеется 11 вариантов
Если у Me есть много вариантов E, то они упорядочиваются по предпочтительности в следующем порядке.
Пусть Env1 и Env2 — два варианта Me в P. Рассмотрим все вхождения переменных в Me. Если Env1 и Env2 не совпадают, они приписывают некоторым переменным различные значения. Найдем в P самое первое слева вхождение переменной, которому Env1 и Env2 приписывают разные значения и сравним длину этих значений. Та из
Например, сопоставим объектное выражение (A1 A2 A3)(B1 B2) с образцом e.1 (e.X s.A e.Y) e.2. В результате получится следующее множество вариантов сопоставления:
{e.1 = , eX = , sA = A1, eY = A2 A3, e.2 = (B1 B2) }
{e.1 = , eX = A1, sA = A2, eY = A3, e.2 = (B1 B2) }
{e.1 = , eX = A1 A2, sA = A3, eY = , e.2 = (B1 B2) }
{e.1 = (A1 A2 A3), eX = , sA = B1, eY = B2, e.2 = }
{e.1 = (A1 A2 A3), eX = B1, sA = B2, eY = , e.2 = }
Варианты сопоставления перечислены в соответствии с их приоритетами, т. е. самый первый вариант находится на первом месте и т. д. Описанный способ упорядочения вариантов сопоставления называется сопоставлением слева направо.
Тот алгоритм
Вхождения атомов, скобок и переменных будут называться элементами выражений. Пропуски между элементами будут называться узлами. Сопоставление E: P определяется как процесс отображения, или проектирования, элементов и узлов образца P на элементы и узлы объектного выражения E. Графическое представление успешного сопоставления приведено на рис. 5.2. Здесь узлы представлены знаками o.
Следующие требования являются инвариантом алгоритма сопоставления и их выполнение обеспечивается на каждой его стадии.
N2 расположен в P правее узла N1, то проекция N2 в E может либо совпадать с проекцией N1, либо располагаться справа от нее (линии проектирования не могут пересекаться).
(рис 5.2) Сопоставление E : P является отображением P на E. Здесь объектным выражением E является 'A'((2'B'))'B', а образцом P является 'A'(e.1 t.2)s.3Предполагается, что в начале сопоставления граничные узлы P отображаются в граничные узлы E. Процесс отображения описывается при помощи следующих шести правил. На каждом шаге отображения правила 1–4 определяют следующий элемент, подлежащий отображению; таким образом, каждый элемент из P получает при отображении уникальный номер.
На рис. 5.2 сопоставление производится следующим образом. Вначале имеется два жестких элемента с одним отображенным концом: 'A' и s.3. В соответствии с Правилом 3 отображается 'A', и этот элемент получает при отображении номер 1.Номера 2 и 3 будут назначены левой и правой скобкам согласно Правилам 3 и 1. Внутри скобок начинается перемещение справа налево, так как t.2 является жестким элементом, который может быть отображен, в то время как значение e.1 еще не может быть определено. На следующем шаге обнаруживается, что e.1 является закрытой переменной, чью проекцию не требуется обозревать для того, чтобы присвоить ей значение; что бы ни было между двумя узлами, это годится для присвоения (на самом деле, значение e.1 оказывается пустым). Отображение s.3 завершает сопоставление. Расположение отображающих номеров над элементами образца дает наглядное представление описанного алгоритма:
1 2 5 4 3 6 'A' ( e.1 t.2 ) s.3
Этот сложный алгоритм упрятан в простые программные конструкции.
Программа на
Имя функции {Последовательность сопоставлений}
Каждое сопоставление имеет вид
Образец = Метавыражение;
Относительное расположение членов последовательности определений никакой роли не играет, и функции можно группировать из логических или технологических соображений. Относительное расположение сопоставлений внутри определения функции существенно.
Пример 5.3.2. Рассмотрим пример
Pre_alph {
*1. Отношение рефлексивно
s.1 s.1 = T;
*2. Если буквы различны, проверить, входит ли
* первая из них в алфавит до второй
s.1 s.2 = <Before s.1 s.2 In <Alphabet>>; }
Before {
s.1 s.2 In e.A s.1 e.B s.2 e.C = T;
e.Z = F; }
Alphabet {
= 'abcdefghijklmnopqrstuvwxyz'; }
Строки, начинающиеся с *, служат комментариями. Последняя из функций введена для технологичности, чтобы определение алфавита было в одном месте и его легко было изменять. Так что функция с пустым образцом может пониматься как константное выражение. In является атомом-разделителем, заведомо не встречающимся в алфавите.
Последнее из правил сопоставления в Before применимо всегда. Такое сопоставление гарантирует, что предикат никогда не заканчивается неудачей.
Если взаимное расположение функций никакой роли не играет, то внутри функции расположение сопоставлений важно. Сначала применяется первое из сопоставлений, при неудаче переходят ко второму и так далее до последнего.
Заметим, что в языке e.A=Обработка ошибки ;
Имеются также встроенные функции, в частности, функции работы с числами <'+' s.Number1 s.Number2> и подобные ей.
Рассмотрим связь между языком и программным окружением.
Для запуска
<Go >
Таким образом, вначале применяется функция Go к пустому Go выглядит примерно следующим образом:
$ENTRY Go { =<Prog <Open 'r' 1 'data.txt'><Get 1> >}
В нашей программе 5.3.1 алфавит определен статически. Было бы хорошо иметь возможность заменять эту глобальную информацию. Для хранения динамической глобальной информации (чаще всего числовых характеристик либо словарей) в языке
<Br e.N '=' e.Е >
рассматривает свой первый аргумент (который должен быть строкой символов, не включающей '=' ) как имя стека, и помещает свой второй аргумент на вершину этого стека. Если стек был пуст, то он создается. Соответственно, функция
<Dg e.N>
выкапывает верхушку стека. Если стек пуст, то ошибки нет, просто выдается пустое выражение. Несколько других функций дополняют возможности работы с глобальной информацией. Cp копирует верхушку стека без ее удаления, замещает верхушку стека на свой аргумент, DgAll выкапывает сразу весь стек.
Ввод-вывод организован в 'r' и присваивает ему номер. Одна строка символов из файла читается с помощью функции Get, заменяющей свой вызов на прочитанную строку, одна строка пишется в файл путем функций
<Put s.Channel e.Expression>
либо
<Putout s.Channel e.Expression>
Вторая функция стирает свое
Следует заметить, что эти функции читают и пишут именно последовательности символов. При их использовании программист должен сам преобразовать последовательности цифр в числа, а скобки-символы в
<Input s.Channel> или <Input e.File-name>
<Xxin e.File-name> или <Xxin s.Channel>
<Xxout s.Channel e.Expr> или
<Xxout (e.File-name)e.Expr>
Первая из функций предназначена для ввода файлов, подготовленных вручную. Вторая и третья — для обмена промежуточной информацией с диском.
Только что перечисленные функции вместе с функцией Go требуют объяснения инструментов модульности в Go. Функции, предоставляемые в пользование другим модулям, описываются спецификатором $ENTRY как входы. В свою очередь, использующий модуль должен описать
$EXTRN F1,F2,F3;
Вызов программы, состоящей из нескольких модулей, производится оператором примерно следующего вида:
refgo prog1+functions+reflib
Модуль основной программы должен идти первым. Никаких средств включить требование вызова модуля в текст другого модуля нет, модули сопрягаются внешним образом. При конфликтах имен берется определение функции из первого в порядке
В частности, только что описанные
Важнейшими средствами современного Mu, которая заключает свой аргумент в Mu работает так, как работало бы определение
Mu { s.F e.X = <s.F e.X> },
если бы оно было синтаксически допустимо.
В частности, через Mu работает стандартный модуль Upd, которая должна быть добавлена к модулю, где осуществляется динамическое вычисление выражений. Например, если добавить описание
$ENTRY Upd {e.X = <Mu e.X>;}
то командная строка refgo e+prog1 приведет к требованию написать выражение. Это выражение будет сделано prog1 и вычислено, а результат выведен. Например, написав для программы 5.3.1
<Alphabet>
мы получим в качестве результата
'abcdefghijklmnopqrstuvwxyz'
Естественно возникает вопрос об обработке внутри языка не только объектных, но и произвольных выражений. Для этого имеются стандартные функции Up и . Первая из них превращает объектное выражение в выражение произвольного вида, вторая кодирует свою область зрения (ею, по семантике языка, может быть лишь объектное выражение) в форме, годящейся для общих выражений и даже
При решении сложных задач на )(. Открывающую скобку высшего уровня представляем как ((, место, куда ведет ссылка — как ))(, закрывающую скобку высшего уровня как )).
И в заключение рассмотрим достаточно сложный алгоритм на
Пусть у нас дано выражение с различными парными скобками (в конкретном случае мы используем пары '()[]{}<>', но программа составлена так, чтобы эти пары можно было заменить в любой момент). Для эффективной работы на
При записи данного алгоритма используется еще одна возможность
Иерархически вложенные функции и условия в принципе не нужны для
$ENTRY Go{=<Init>;}
$EXTRN Xxout;
* Инициализация поля зрения и констант
Init{=<Open 'r' 1 'Input.txt'><Trans <Acquire(<Get 1>)";}
Acquire {
e.Got ()= e.Got;
* Конец ввода - пустая строка
e.Got (e.New)=<Acquire e.Got e.New (<Get 1>)>;
}
Brackets {=('()')('[]')('')('<>');}
Trans {
e.A =<Result <Pairing () e.A > >;
}
Pairing {
* В первой скобке содержится последовательность всех незакрытых
* скобок вместе с сегментами данных, подлежащими помещению
* в даннуюпару скобок;
* каждый сегмент данных также заключен в скобки
(e.Unclosed (e.LastUn)(s.Lbrack e.Middle)) s.Rbrack e.Last,
<Brackets>: e.A(s.Lbrack s.Rbrack ) e.B =
* Встретилась правая скобка, парная последней незакрытой;
* выбрасываем отработанный сегмент из поля зрения
<Pairing (e.Unclosed (e.LastUn (s.Lbrack e.Middle s.Rbrack))) e.Last>;
((s.Lbrack e.Middle)) s.Rbrack e.Last,
<Brackets>: e.A(s.Lbrack s.Rbrack ) e.B =
* Парная незакрытой, находящейся внутри другой незакрытой
(s.Lbrack e.Middle s.Rbrack) <Pairing () e.Last>;
(e.Unclosed (e.LastUn)(s.Lbrack e.Middle)) s.Rbrack e.Last,
<Brackets>: e.A(s.Lbrack1 s.Rbrack ) e.B =
* Непарные скобки
<Prout "Brackets Mismatch!"> Error;
(e.Unclosed ) s.Lbrack1 e.Last,
<Brackets>: e.A(s.Lbrack1 s.Rbrack ) e.B =
* Еще одна открывающая скобка; создаем новую группу данных
<Pairing (e.Unclosed (s.Lbrack1)) e.Last>;
() s.Lbrack e.Last,
<Brackets>: e.A(s.Lbrack s.Rbrack ) e.B =
* Первая открывающая скобка
<Pairing ((s.Lbrack)) e.Last>;
() s.Rbrack e.Last,
<Brackets>: e.A(s.Lbrack s.Rbrack ) e.B =
* Первая скобка - закрывающая
<Prout "Extra right bracket"> Error;
* Нейтральный символ вне скобок
() s.Other e.Last = s.Other <Pairing () e.Last>;
* Выражение и скобки исчерпаны - успех
() =;
* Выражение исчерпано, а скобки - нет
(e.Unclosed (e.Lastun))=<Prout "Not all brackets are closed" Error;
* Нейтральный символ в очередной скобке
(e.Unclosed (e.Lastun))s.Other e.Last=
<Pairing (e.Unclosed(e.Lastun s.Other)) e.Last>;
}
Result {
* Если была ошибка, выйти
e.A Error=;
* Иначе вывести результат для дальнейшего использования
e.A =<Xxout ('output.rdt') e.A>;
}
Для того, чтобы нагляднее увидеть влияние стиля на программные решения, сравните эту программу с развитием программы в традиционном стиле, приведенным в § 11.5 книги . Данная программа намного выразительнее, короче, легче модифицируема и не менее эффективна, чем программа 11.5.3 из указанного параграфа.
В практике работы со студентами автору приходилось сталкиваться с ситуацией, когда программа для одной и той же задачи, написанная на традиционном языке, была более чем в пять раз длиннее программы на
Из литературы по языку
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.