Я тоже начал было писать про "настоящее выворачивание", но Александр меня опередил. Да, это оно самое. Мне такой стиль не нравится, и сам я так не пишу. Возможно, потому, что Рефал-6 позволяет не экономить на копированиях, и я предпочитаю рекурсивный стиль. Но хочу высказаться в защиту этой формы, которую использовал Турчин. Думаю, тут дело не в экономии копирований, а в стиле мышления. Как ни странно, Турчин рассматривал работу рефал-программы как пошаговый процесс с изменяющимся состоянием. Отсюда и понятие поля зрения. Да, оно моделирует стек, неявно возникающий при рекурсии. Но при мета-деятельности Турчин предпочитал поле зрения объектной программы держать как бы целиком перед глазами. В центре - активная конкретизация, а по краям - окружение. Отсюда и возник такой стиль. Он в каком-то смысле сближает рефал с машиной Тьюринга, только вместо линейной ленты - дерево. Когда-то около 90 года я попытался с ним на эту тему заговорить, как бы предлагая перейти на рекурсивный стиль, но он как-то это сходу отверг, мол так ему удобнее. Разговора по существу не получилось. Поэтому сейчас я его так и понимаю, как написал выше.
С уважением, Аркадий пн, 14 дек. 2020 г. в 16:08, Александр Коновалов a.v.konovalov87_AT_mail.ru <refal@botik.ru>: > Василий, не надо пошаговую прокрутку. Я неправильно посчитал скобки. > Во входной строке их не 4, а 3, поэтому всё сходится: 3+1=4. > > > > *From:* Василий Стеллецкий swi_AT_cnshb.ru <refal@botik.ru> > *Sent:* Monday, December 14, 2020 4:58 PM > *To:* refal@botik.ru > *Subject:* Re: Запахи кода и антипаттерны в Рефале > > > > Александр. > Действительно. > Еще раз, Большое спасибо. > PS Если хотите, могу прислать пошаговую прокрутку обоих вариантов... > > > > -- > С уважением, > > -- > > Василий Стеллецкий > > mailto:s...@cnshb.ru <sw...@narod.ru> mailto:sw...@narod.ru > <sw...@narod.ru> > > > > > > > > 14.12.2020, 16:38, "Александр Коновалов a.v.konovalov87_AT_mail.ru" < > 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> > *Sent:* Monday, December 14, 2020 4:05 PM > *To:* refal@botik.ru > *Subject:* Re: Запахи кода и антипаттерны в Рефале > > > > Александр! > Большое спасибо за подробный рассказ! > Но, то что в примере применили Вы - тоже самое: Справа стек. > Только просмотренную часть Вы не собирали в левой скобке, а сразу выводили > за пределы функции... > Точнее стек получался из вызовов DoEnum-Wrap и скобок наизнанку ... > Еще раз, Большое спасибо! > P.S. А в этом варианте получился 31 шаг. > 4 шага где-то сэкономили ;) > > > > -- > С уважением, > > -- > > Василий Стеллецкий > > mailto:s...@cnshb.ru <sw...@narod.ru> mailto:sw...@narod.ru > <sw...@narod.ru> > > > > > > > > 14.12.2020, 15:23, "Александр Коновалов a.v.konovalov87_AT_mail.ru" < > 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> > *Sent:* Monday, December 14, 2020 1:52 PM > *To:* 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 <sw...@narod.ru> mailto:sw...@narod.ru > <sw...@narod.ru> > > > > > > > > 14.12.2020, 13:25, "Александр Коновалов a.v.konovalov87_AT_mail.ru" < > 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> > *Sent:* Monday, December 14, 2020 12:47 PM > *To:* 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 <sw...@narod.ru> mailto:sw...@narod.ru > <sw...@narod.ru> > > > > > > > > 14.12.2020, 10:47, "Александр Коновалов a.v.konovalov87_AT_mail.ru" < > 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> > Sent: Monday, December 14, 2020 4:27 AM > To: Александр Коновалов a.v.konovalov87_AT_mail.ru <refal@botik.ru> > Subject: Re: Запахи кода и антипаттерны в Рефале > > День добрый, всем! > > > А какие вы можете назвать запахи кода и антипаттерны, характерные для > Рефала? > > > Все, что вскрывает (или скрывает?) недостатки используемого представления > объектных выражений и приводит к чудовищному (для > пониманию) кода: > > 1. Выворачивание скобок наизнанку, для организации прохода по выражению. > > 2. Боязнь копирования кусков выражений в разные "дочерние" функции. > > Этим страдают многия языки. Например, в Эрланге целый культ на тему > "стремись к хвостовой рекурсии!" и "пиши с аккумулятором, а потом выдай > результат, навесив на него reverse (Который, видимо, у них написан на не > Erlang-е)". > > Если мы стремимся к самодокументированному коду без комментариев, то такие > фенечки должны не приветствоваться. > > С уважением, > > Абрамов С.М. > ab...@botik.ru > мобильный: +7(903)2928308 > > > -- _______________ *С уважением, * *Аркадий Климов,* *с.н.с. ИППМ РАН,* *+7(499)135-32-95* *+7(916)072-81-48*