Доброй ночи, Николай! Сквозной просмотр был у Турчина в учебнике по Рефалу-5:
Глава 3. <http://refal.ru/chap3_r5.html#3.7.%20%D0%A0%D0%90%D0%91%D0%9E%D0%A2%D0%90%20%D0%A1%20%D0%92%D0%9B%D0%9E%D0%96%D0%95%D0%9D%D0%9D%D0%AB%D0%9C%D0%98%20%D0%A1%D0%9A%D0%9E%D0%91%D0%9A%D0%90%D0%9C%D0%98> Основные приемы программирования (koi8-r) (refal.ru) Да, и одним из примеров является спаривание скобок. Ещё этот приём используется в суперкомпиляторах SCP3 и SCP4. Можно исходники скачать и убедиться. С уважением, Александр Коновалов From: nikolai.kondratiev_AT_gmail.com [mailto:refal@botik.ru] Sent: Monday, December 14, 2020 10:35 PM To: refal@botik.ru Subject: AW: Запахи кода и антипаттерны в Рефале Я вспоминаю, что Турчин был очень доволен тем, что нашел алгоритм СПАРИВАНИЯ СКОБОК, работающий линейное время, а сквозной просмотр появился как вариация на ту же тему. Мне кажется, что сам сквозной просмотр он не слишком пропагандировал. Von: Arkady Klimov arkady.klimov_AT_gmail.com <refal@botik.ru <mailto:refal@botik.ru> > Gesendet: Montag, 14. Dezember 2020 17:49 An: refal@botik.ru <mailto:refal@botik.ru> Betreff: Re: Запахи кода и антипаттерны в Рефале Я тоже начал было писать про "настоящее выворачивание", но Александр меня опередил. Да, это оно самое. Мне такой стиль не нравится, и сам я так не пишу. Возможно, потому, что Рефал-6 позволяет не экономить на копированиях, и я предпочитаю рекурсивный стиль. Но хочу высказаться в защиту этой формы, которую использовал Турчин. Думаю, тут дело не в экономии копирований, а в стиле мышления. Как ни странно, Турчин рассматривал работу рефал-программы как пошаговый процесс с изменяющимся состоянием. Отсюда и понятие поля зрения. Да, оно моделирует стек, неявно возникающий при рекурсии. Но при мета-деятельности Турчин предпочитал поле зрения объектной программы держать как бы целиком перед глазами. В центре - активная конкретизация, а по краям - окружение. Отсюда и возник такой стиль. Он в каком-то смысле сближает рефал с машиной Тьюринга, только вместо линейной ленты - дерево. Когда-то около 90 года я попытался с ним на эту тему заговорить, как бы предлагая перейти на рекурсивный стиль, но он как-то это сходу отверг, мол так ему удобнее. Разговора по существу не получилось. Поэтому сейчас я его так и понимаю, как написал выше. С уважением, Аркадий пн, 14 дек. 2020 г. в 16:08, Александр Коновалов a.v.konovalov87_AT_mail.ru <http://a.v.konovalov87_AT_mail.ru> <refal@botik.ru <mailto:refal@botik.ru> >: Василий, не надо пошаговую прокрутку. Я неправильно посчитал скобки. Во входной строке их не 4, а 3, поэтому всё сходится: 3+1=4. From: Василий Стеллецкий swi_AT_cnshb.ru <refal@botik.ru <mailto:refal@botik.ru> > Sent: Monday, December 14, 2020 4:58 PM To: refal@botik.ru <mailto:refal@botik.ru> Subject: Re: Запахи кода и антипаттерны в Рефале Александр. Действительно. Еще раз, Большое спасибо. PS Если хотите, могу прислать пошаговую прокрутку обоих вариантов... -- С уважением, -- Василий Стеллецкий mailto:s...@cnshb.ru <mailto:sw...@narod.ru> mailto:sw...@narod.ru 14.12.2020, 16:38, "Александр Коновалов a.v.konovalov87_AT_mail.ru <http://a.v.konovalov87_AT_mail.ru> " <refal@botik.ru <mailto:refal@botik.ru> >: Василий! Разница тут принципиальная. В первом варианте стек был системный, программист писал рекурсивную функцию, явно со стеком не манипулировал. Поскольку программист за стек не отвечает, сломаться стек не может. Во втором случае — стеком явно управляет программист. И он может ошибиться, например, положить состояние на один стек и не положить на другой. Или снять состояние только с одного стека. В любом случае стеки рассинхронизируются. Или допустить ошибку в формате, перепутав стек с обрабатываемыми данными (например, перепутав tL и tR). a =/0/k/Enum/ 'abc' (('de') 'f' ('gh') 'ij') 'k'. Enum e1 = k/EndEnum/k/DoEnum/e1 /1/.. DoEnum s1 e2 sN = sN k/DoEnum/ e2 k/P1/sN.. (e1)e2 sN = k/DoEnum-Wrap/ (k/DoEnum/ e1 sN.) e2. sN = sN DoEnum-Wrap (e1 sN) e2 = (e1) k/DoEnum/ e2 sN. EndEnum e1 sN = e1 И нету оборачивания скобки в рекурсивном решении. Один шаг сэкономился на EndEnum, т.к. в последнем предложении мы и стеки отбрасываем, и счётчик. Ещё шаги экономятся на обработке скобок. Открывающая скобка в обоих вариантах требует одного шага. Закрывающая скобка — двух шагов: последнее предложение DoEnum, где она видит аргумент с пустой строкой и счётчиком и DoEnum-Wrap, которая выбрасывает вон скобку и продолжает цикл DoEnum. Но закрывающих скобок 4, должно сэкономиться 4 шага. И один на EndEnum. 4+1≠4, где-то что-то я не учёл. С уважением, Александр Коновалов From: Василий Стеллецкий swi_AT_cnshb.ru <refal@botik.ru <mailto:refal@botik.ru> > Sent: Monday, December 14, 2020 4:05 PM To: refal@botik.ru <mailto:refal@botik.ru> Subject: Re: Запахи кода и антипаттерны в Рефале Александр! Большое спасибо за подробный рассказ! Но, то что в примере применили Вы - тоже самое: Справа стек. Только просмотренную часть Вы не собирали в левой скобке, а сразу выводили за пределы функции... Точнее стек получался из вызовов DoEnum-Wrap и скобок наизнанку ... Еще раз, Большое спасибо! P.S. А в этом варианте получился 31 шаг. 4 шага где-то сэкономили ;) -- С уважением, -- Василий Стеллецкий mailto:s...@cnshb.ru <mailto:sw...@narod.ru> mailto:sw...@narod.ru 14.12.2020, 15:23, "Александр Коновалов a.v.konovalov87_AT_mail.ru <http://a.v.konovalov87_AT_mail.ru> " <refal@botik.ru <mailto:refal@botik.ru> >: Василий! Выворачивание скобок, оно же сквозной просмотр, — это другое. Это использование стека для представления незакрытых скобок. Например, так: Enum e1 = k/DoEnum/ /0/ ('$') e1 '$'. * Формат: k/DoEnum/ sN (tL eS) eU tR * eS — просканированная часть * eU — непросканированная часть * tL — стек просканированных частей слева * tR — стек непросканированных частей справа * символ просто переносим DoEnum sN (tL eS) s1 eU tR = k/DoEnum/ k/P1/sN. (tL eS sN) eU tR. * в скобочный терм спускаемся sN (tL eS) (e1) eU tR = k/DoEnum/ sN ((tL eS)) e1 (eU tR). * термы кончились, но стек не пустой sN ((tL eS) e1) (eU tR) = k/DoEnum/ sN (tL eS (e1)) eU tR. * термы кончились и стек тоже пустой — выход из цикла sN ('$' eS) '$' = eS Почему здесь скобки наизнанку? Потому что для частично просмотренного выражения вида e1 (e2 (e3 ^ e4) e5) e6 где знак ^ означает позицию, вызов функции DoEnum будет иметь вид k/DoEnum/ ((('$' e1) e2) e3) e4 (e5 (e6 '$')). Т.е. незакрытые скобки перед ^ оказываются закрывающими, а неоткрытые после ^ — открывающими. А скобка между e3 и e4 служит указателем ^. У Турчина в учебнике по Рефалу-5 предлагается немного другой способ кодирования стека, но он не менее страшный, чем этот. Сам Турчин, похоже, осознавал неестественность такого программирования, поэтому в комплекте с Рефалом-5 поставляется препроцессор для сквозного просмотра скобок. Как по мне, лучше использовать рекурсию для обхода рекурсивных структур, чем наворачивать такие стеки. С уважением, Александр Коновалов From: Василий Стеллецкий swi_AT_cnshb.ru <refal@botik.ru <mailto:refal@botik.ru> > Sent: Monday, December 14, 2020 1:52 PM To: refal@botik.ru <mailto:refal@botik.ru> Subject: Re: Запахи кода и антипаттерны в Рефале Добрый день, Александр! Да, шагов поменьше! a =/0/k/Enum/ 'abc' (('de') 'f' ('gh') 'ij') 'k'. Enum e1 = k/EndEnum/k/DoEnum/e1 /1/.. DoEnum s1 e2 sN = sN k/DoEnum/ e2 k/P1/sN.. (e1)e2 sN = k/DoEnum-Wrap/ k/DoEnum/ e1 sN. (e2). sN = sN DoEnum-Wrap e1 sN (e2) = (e1) k/DoEnum/ e2 sN. EndEnum e1 sN = e1 S:35 cc=0 M=1024 ПЗ: /0/ /1/ /2/ /3/ ( ( /4/ /5/ ) /6/ ( /7/ /8/ ) /9/ /10/ ) /11/ КОП: Спасибо, понял что такое "выворачивание скобок" :) -- С уважением, -- Василий Стеллецкий mailto:s...@cnshb.ru <mailto:sw...@narod.ru> mailto:sw...@narod.ru 14.12.2020, 13:25, "Александр Коновалов a.v.konovalov87_AT_mail.ru <http://a.v.konovalov87_AT_mail.ru> " <refal@botik.ru <mailto:refal@botik.ru> >: Добрый день, Василий! А если мы пишем для многопоточного Рефала, то статический ящик или копилка не подойдёт. Нужно использовать или динамический ящик, или передавать номер через параметр. Получится как-то так: Enum e1 = k/DoEnum/e1 /0/. DoEnum s1 e2 sN = sN k/DoEnum/ e2 k/P1/sN.. (e1)e2 sN = k/DoEnum-Wrap/ k/DoEnum/ e1 sN. (e2). DoEnum-Wrap e1 sN (e2) = (e1) k/DoEnum/ e2 sN. Если что, я не запускал этот код, могут быть опечатки. Также это решение эффективнее по числу шагов, чем вариант с копилкой. С уважением, Александр Коновалов From: Василий Стеллецкий swi_AT_cnshb.ru <refal@botik.ru <mailto:refal@botik.ru> > Sent: Monday, December 14, 2020 12:47 PM To: refal@botik.ru <mailto:refal@botik.ru> Subject: Re: Запахи кода и антипаттерны в Рефале Добрый день всем! Я про задачку... Ну, на мой взгляд не просто для понимания... На других диалектах Рефала - да пожалуйста! В этой задаче удобно использовать Ящики или Копилку. Например с копилкой: a =/0/k/Enum/ 'abc' (('de') 'f' ('gh') 'ij') 'k'. Enum s1e2=k/Enum1/k/ВК/'n'..k/Enum/e2. (e1)e2=(k/Enum/e1.)k/Enum/e2. = Enum1 =/1/k/ЗК/'n='/2/. sn=snk/ЗК/'n='k/P1/sn.. Результат: S:62 cc=0 M=1024 ПЗ: /0/ /1/ /2/ /3/ ( ( /4/ /5/ ) /6/ ( /7/ /8/ ) /9/ /10/ ) /11/ КОП: ( 'n' '=' /12/ ) (первый /0/ в поле зрения не имеет отношения к задаче, это мой интерфейс) -- С уважением, -- Василий Стеллецкий mailto:s...@cnshb.ru <mailto:sw...@narod.ru> mailto:sw...@narod.ru 14.12.2020, 10:47, "Александр Коновалов a.v.konovalov87_AT_mail.ru <http://a.v.konovalov87_AT_mail.ru> " <refal@botik.ru <mailto:refal@botik.ru> >: Доброе утро всем! 1. Выворачивание скобок наизнанку, для организации прохода по выражению. К представлению объектных выражений этот приём (антиприём) не имеет отношение. Он, скорее, характерен для базисного Рефала (Рефала-2), в котором нет ничего подобного let-конструкциям (условия, перестройки, действия). И благодаря этому сквозному обходу исчезает нужда во вспомогательных функциях, можно обойтись одной. Но, вообще этот приём уродлив, хоть и пропагандировался Турчиным. 2. Боязнь копирования кусков выражений в разные «дочерние» функции. А в Рефале-5 ещё и в условия. У меня в Рефале-5λ копирование выражений дорогое, но я копировать их по умолчанию не боюсь. Потому что потом я нахожу узкие места, изучая профиль программы, и устраняю лишние копирования уже в них. А нет ли в Рефале Плюс другой боязни — боязни конкатенации? Задача. Заменить в выражении все символы последовательными натуральными числами: <Enum 'abc' (('de') 'f' ('gh') 'ij') 'k'> → 1 2 3 ((4 5) 6 (7 8) 9 10) 11 Решение на Рефале-5λ, эффективное (O(n)) и простое для понимания (на мой взгляд): Enum { e.Expr = <DoEnum e.Expr 1> : e.Expr^ s.Num = e.Expr; } DoEnum { /* пусто */ s.Num = s.Num; s.X e.Expr s.Num = s.Num <DoEnum e.Expr <+1 s.Num>>; (e.Nested) e.Expr s.Num = <DoEnum e.Nested s.Num> : e.Nested^ s.Num^ = (e.Nested) <DoEnum e.Expr s.Num>; } Конструкции = … : … в Enum и в последнем предложении DoEnum неявно транслируются в вызов вспомогательной функции. Знак ^ после имени переменной в образце означает, что она не повторная. Представление данных — плоское списковое, то самое с дорогим копированием и копеечной конкатенацией. Как будет выглядеть O(n) решение в других диалектах Рефала? С уважением, Александр Коновалов -----Original Message----- From: Sergei M. Abramov abram_AT_botik.ru <refal@botik.ru <mailto:refal@botik.ru> > Sent: Monday, December 14, 2020 4:27 AM To: Александр Коновалов a.v.konovalov87_AT_mail.ru <http://a.v.konovalov87_AT_mail.ru> <refal@botik.ru <mailto:refal@botik.ru> > Subject: Re: Запахи кода и антипаттерны в Рефале День добрый, всем! А какие вы можете назвать запахи кода и антипаттерны, характерные для Рефала? Все, что вскрывает (или скрывает?) недостатки используемого представления объектных выражений и приводит к чудовищному (для пониманию) кода: 1. Выворачивание скобок наизнанку, для организации прохода по выражению. 2. Боязнь копирования кусков выражений в разные "дочерние" функции. Этим страдают многия языки. Например, в Эрланге целый культ на тему "стремись к хвостовой рекурсии!" и "пиши с аккумулятором, а потом выдай результат, навесив на него reverse (Который, видимо, у них написан на не Erlang-е)". Если мы стремимся к самодокументированному коду без комментариев, то такие фенечки должны не приветствоваться. С уважением, Абрамов С.М. ab...@botik.ru <mailto:ab...@botik.ru> мобильный: +7(903)2928308 -- _______________ С уважением, Аркадий Климов, с.н.с. ИППМ РАН, +7(499)135-32-95 +7(916)072-81-48