Добрый вечер, Александр!
«Возможным решением вижу использование функций без побочных эффектов в левой
части. Помню упоминание о такой возможности где-то в препринтах Турчина.»
Да, это было у Турчина в «пятитомнике» препринтов о языке Рефал. Переменные,
сопоставление с которыми приводит к вызову функции, там назывались
«рекурсивными». Они ни разу не были реализованы, на сколько мне известно.
Одной из наиболее распространённых потребностей в рекурсивных переменных была
потребность в ограничении множества значений термов, сопоставимых с переменной.
Например, «эта s-переменная может сопоставиться только с буквой», «эта
e-переменная может сопоставиться только с последовательностью цифр», «эта
s-переменная не может быть равна символу 'ERR'», «эта v-переменная (непустая
e-переменная) состоит только из макроцифр» и т.д. Данную задачу успешно решали
спецификаторы, появившиеся в Рефале-2.
Позже, в Рефале-5 появились условия, которые по возможностям были сравнимы со
спецификаторами, поэтому в Рефале-5 и последующих диалектах (кроме одного, см.
далее) ничего, подобного спецификаторам, не появлялось. Практика показала, что
условия и эквивалентные им конструкции в Рефале-6 и Рефале Плюс дают
удовлетворительный результат.
Рефал-6 (не Рефал-6b, о котором недавно писал Николай, а тот, который развил и
поддерживает Аркадий Климов) содержит понятия «действия» и «неуспехи». В письме
я рассказывать про них не буду, отошлю к документации, которая достаточно
прозрачна. В Рефале-6 есть возможность выразить и связку «или» (как блок с
несколькими предложениями), и связку «и» (как несколько условий), и связку «не»
(есть отрицания действий), и скобки (усиления и ослабления неуспехов).
Вариативность в левой части образца в Рефале-6 можно выразить следующим образом:
F {
e.Name 〈что-то ещё〉, e.Name { 'Маша' = ; 'Саша' = } = … ;
… = …;
};
Если в синтаксисе что-то напутал, Аркадий Валентинович может поправить.
Аналогичная функциональность есть и в Рефале Плюс, но только там синтаксис и
семантика сложнее. Одной из целей Рефала Плюс было написание языка, который был
бы замкнут относительно суперкомпиляции полного Рефала (возможно даже, это была
основная цель). Но результат суперкомпиляции выражается не в виде программы на
Рефале, а в виде графа суперкомпиляции, существенно более низкоуровневой
конструкции. Поэтому диалект Рефал Плюс обладает рядом низкоуровневых средств
вроде функций с несколькими аргументами и результатами (полные покрывающие
функции в это прокрустово ложе не ложатся) с ограничениями на их использование
(несоответствие формату вызывает ошибку компиляции), тонкими средствами
управления потоком выполнения — заборами, отсечениями, прозрачными и
непрозрачными блоками и прочим. В общем, то, что оптимизирующий компилятор в
идеале должен делать неявно, программист в Рефале Плюс может прописать явно.
Рефал-6 гораздо более высокоуровневый язык, он допускает программирование как в
низкоуровневом стиле Рефала Плюс, так и в высокоуровневом стиле Рефала-5. И его
понять гораздо проще. Я сам сначала разобрался в Рефале-6, а потом в Рефале
Плюс.
Вариативность (но с ущербом для эффективности) можно реализовать и в Рефале-5.
Код, эквивалентный коду Рефала-6, будет выглядеть так:
F {
e.Name 〈что-то ещё〉, <F-Name e.Name> : T = … ;
… = …;
}
F-Name {
'Маша' = T;
'Саша' = T;
e.Other = F;
}
В Рефале-5λ тоже самое можно записать компактнее за счёт синтаксического сахара:
F {
e.Name 〈что-то ещё〉
, e.Name
: {
'Маша' = T;
'Саша' = T;
e._ = F;
}
: T
= … ;
… = …;
}
Теперь возвращаюсь к тому «одному диалекту». Это диалект Refal-D, предложенный
Маратом Шамильевичем Исламовым. Он отчасти наследует от Рефала-5 (условия
остались, блоки выкинуты), однако в языке был существенно расширен синтаксис
правых частей. Множества значений переменных в нём можно задавать фактически
при помощи грамматик, заданных в БНФ. В частности, пример с Машей-Сашей на нём
можно записать так:
F {
{ 'Маша' | 'Саша' }.Name 〈что-то ещё〉 = … ;
… = …;
}
Доступна презентация об этом языке:
http://freeschool.altlinux.ru/wp-content/uploads/2010/02/islamov_presentation.pdf
Есть описание диалекта в рефал-сборнике:
http://refal.botik.ru/library/refal2015_issue-II.pdf
Есть репозиторий исходных текстов:
https://github.com/mislamov/refal51
Реализация, на мой взгляд, сырая, но идеи, заложенные в языке, стоят изучения.
На мой взгляд, это современное и безопасное развитие ранней идеи Турчина о
рекурсивных переменных.
А вообще, для данной конкретной задачи можно написать так:
F {
e.Name 〈что-то ещё〉, ('Маша') ('Саша') : e.1 (e.Name) e.2 = … ;
… = …;
}
Если писать в базисном Рефале, без условий, то так:
F {
e.Name 〈что-то ещё〉 = <F1 ('Маша') ('Саша') (e.Name 〈что-то ещё〉)> ;
… = …;
}
F1 {
e.1 (e.Name) e.2 (e.Name 〈что-то ещё〉) = …;
… = …;
}
Имя для функции F1, на мой вкус, плохое, но в зависимости от задачи можно
подобрать и более подходящее имя.
С уважением,
Александр Коновалов
From: Александр Гусевgusev_aleksandr_AT_mail.ru
Sent: Saturday, March 27, 2021 10:37 PM
To: refal
Subject: Регулярные выражения слева
Коллеги,
Прошу извинения, если я просмотрел что-то, но давно возник вопрос, ответа пока
не увидел.
При обработке текстов весьма удобно иметь вариантность в левой части образца,
которую дают регулярные выражения. Например, конструкция
(Маша|Саша)
В выражении за одно обращение позволяет выбрать либо одно либо другое имя.
На Рефале-2 это будет два образца, по одному на имя.
‘Маша’ ...=...
‘Саша’ ...=...
Возможным решением вижу использование функций без побочных эффектов в левой
части. Помню упоминание о такой возможности где-то в препринтах Турчина.
Другой вариант - встроить рег.выражения в Рефал. Мне это нравится меньше, так
как оно «не родное», смешиваются параллельные технологии, что не очень хорошо.
Вариант с функциями в левой части также может сильно усложнить понимание кода,
когда нужно быстро разобраться.
Подскажите, пожалуйста, возможно есть и используются другие решения этой
задачи, мне не очевидные. Речь идёт, конечно, о компактных решениях. Как модно
решить задачу длинно, понятно.