Добрый вечер всем! На сколько я знаю, когда-то были планы реализации front-end’а Рефала-5 в компиляторе Рефала Плюс. Т.е. предполагалось использовать компилятор Рефала Плюс для компиляции исходных текстов Рефала-5.
(Дальше пишу подробно, поскольку не все подписчики знакомы с рассматриваемой проблемой. Те, кто знакомы, могут сразу промотать письмо до конца.) Но есть один нюанс. Рефал Плюс использует массивное представление данных с дорогой конкатенацией. А это значит, что традиционный способ передавать параметры в функцию, используя N−1 скобок для N e-параметров будет приводить к избыточной конкатенации, а значит, и увеличению порядка сложности. Пример, замена символов A на B: Fab { e.X = <Loop () e.X> } Loop { (e.Acc) 'A' e.Rest = <Loop (e.Acc 'B') e.Rest>; (e.Acc) s.X e.Rest = <Loop (e.Acc s.X) e.Rest>; (e.Acc) /* пусто */ = e.Acc; } Здесь есть две конкатенации, с которыми столкнётся Рефал Плюс. Первая — конкатенация символа с аккумулятором e.Acc 'B' и e.Acc s.X. Если длина аккумулятора N, то потребуется создать новый массив длины N+1, скопировать в него содержимое e.Acc и дописать туда символ ('B' или s.X). На каждом шаге аккумулятор вырастает на единицу, а значит, затраты времени на копирования элементов будут N×(N+1)/2 = O(N²), где N — исходная длина строки. Вторая — конкатенация скобочного терма с остатком строки (…) e.Rest. В ней затраты те же: нужно создать массив длины N+1 (где N — длина e.Rest), скопировать туда e.Rest и скобочный терм. Тоже сложность будет O(N²). Первая проблема, проблема роста аккумулятора, решается пустым местом вокруг формируемого массива. На большинстве итераций новый символ будет дописан в пустое место, а сам массив копировать не потребуется. Затраты времени на формирование выражения в аккумуляторе будут амортизированными линейными. Детали пересказывать не буду (когда-то их уже обсуждали в рассылке). В общем, решение элегантное. А вот вторую конкатенацию дыры не спасут, т.к. их там нет. В массиве-аргументе слева от e.Rest уже находится символ, вписать на его место круглую скобку нельзя, т.к. участком массива 'A' e.Rest может пользоваться другая часть программы. Эту проблему создатели языка решили жёстко: форматы функций (которые, вообще-то, идиома, рекомендация) сделали частью языка. Теперь программист вынужден для каждой функции указывать формат, образцы всех предложений должны этот формат уточнять, результат работы функции в этот формат тоже обязан вкладываться. Аргументы функций также должны соответствовать входному формату. Для функций выше в программе должны быть написаны следующие строчки: $func Fab e.X = e.X; $func Loop (e.Acc) e.X = e.Acc; Для каждого вызова Loop компилятор проверит, что фактический аргумент вкладывается в формат, после чего аккумулятор и остаток строки передаст в виде двух отдельных параметров. Конкатенации не будет. Сложность функции будет амортизированно линейной. Теперь написать функцию с несколькими форматами или с необязательными параметрами нельзя. Вернее можно, но это будет неэффективно. Так вот, к чему я пишу. На Рефале-5 конкатенация дешёвая, форматов на уровне синтаксиса нет, встречаются функции с несколькими форматами. Как быть с такими программами? Если функциям неявно приписывать формат e.X = e.X, то программы для Рефала-5, скомпилированные Рефалом Плюс, будут работать заведомо медленно. Причём медленнее не на константу, а на порядок алгоритма. А что же тогда предполагалось делать, чтобы программы для Рефала-5 были приемлемо эффективны, будучи скомпилированы Рефалом Плюс? В соседней ветке мы обсуждаем плохие приёмы программирования, и одна из мыслей была — борьба с недостатком представления данных протекает в стиль программирования. Так вот, в Рефале Плюс борьба с недостатком представления данных протекла аж в дизайн языка. С уважением, Александр Коновалов