Здравствуйте, Бойко!

> А вот полным решением проблемы с избыточностью имен в struct в Си я горжусь 
> :) И в учебниках по программированию на Си, и в реальных программах — тонны 
> этой избыточности, но решение показывает, что она не нужна.

Согласен, красиво и компактно. Но с другой стороны, я уже пять лет работаю в 
коммерческом проекте на Си99, и по стилю кода у нас не принято использовать 
typedef. Соответственно, всюду пишем struct Foo, struct Bar * и т.д. И, что 
интересно, я привык к этой конструкции. Из кода видно, что типом переменной 
может быть либо примитивный тип (uint32_t, скажем), структура, указатель на 
что-то (в том числе и указатель на структуру), и как-то без struct уже 
воспринимается неестественно.

> Это если принять, что в структурном программировании их нет.
> Но данная точка зрения не единственная.

Для меня структурное программирование — это использование конструкций с одним 
входом и одним выходом. Классика: присваивание, вызов подпрограммы, следование 
инструкций, if/else, while, do/whie, цикл со счётчиком (for Паскаля), 
подпрограммы с одним return’ом в самом конце. Сюда можно добавить цикл foreach, 
перебирающий элементы итератора. Основные свойства: для каждого оператора 
программы можно синтаксически указать, какие операторы предшествовали, какие 
операторы следуют. В начало цикла можно попасть либо после последнего оператора 
перед циклом, либо после последнего оператора конца цикла. В точку после 
if/else — либо после последнего оператора ветки then, либо ветки else. В 
составном операторе — последовательности инструкций — можно быть уверенным, что 
если управление передалось на её начало, оно обязательно дойдёт до конца. В 
таких структурных программах циклы управляются исключительно своими заголовками 
—не заглядывая в тело цикла, можно назвать условие завершения.

Но, понятно, это сильные ограничения на стиль программирования, и бывают 
задачи, когда чистый структурный код читается и поддерживается хуже, чем 
прагматичный неструктурный. Например, бывают случаи, когда даже goto 
существенно упрощает и проясняет программу (но это не значит, что можно писать 
по одному goto на каждые 10 строк кода, если конечно, речь идёт не о 
ассемблере).

Я, лично, предпочитаю писать структурно, если задача не требует иного. И люблю 
ту идиому с while для поиска.

> Признано, что придерживаться какого-нибудь множества структурных схем нужно, 
> но по-моему так же нужно выбор этого множества время от времени 
> пересматривать.  Иначе в императивном программировании мы бы остались с 
> Фортраном или Алголом-60, у каждого из которых имеется единственный оператор 
> цикла.  А в функциональном программировании так и не пришли бы к лексическому 
> связыванию, частичному применению, одноаргументности и композиции функций и 
> сопоставлению с образцом — остались бы с Лиспом, у которого их и сегодня нет, 
> за исключением лексического связывания.

А они и пересматриваются. Например, в последние годы многие императивные языки 
программирования обрели цикл foreach, перебирающий элементы некоторого 
итератора, характерного для языка. Даже C++, в котором for(type var: cont) {…} 
эквивалентен for(auto p = begin(cont); p != end(cont); ++p) {type var = *p; …}. 
Кстати, цикл foreach иногда реализуют на Си при помощи хитрых макросов. 
Например:
https://github.com/kroki/Cuckoo-hash/blob/master/src/cuckoo_hash.h#L159-L192

> …или Алголом-60, у каждого из которых имеется единственный оператор цикла
Удивился, поскольку помнилось, что в Алголе есть и for, и while. Открыл отчёт 
по Алголу (revised report, 1962) — был очень витиеватый цикл for, который мог 
быть даже while.

for j := 1 step 1       until 10 do — переберёт числа 1…10
for j := 1 step 1 until 10, 20 step 1 until 30 do — переберёт 1…10, 20…30
for j := 2, 3, 5, 7, 11, 13 do — переберёт несколько простых чисел
for j := 42 while x > 7 do — будет повторять тело цикла с параметром 42, пока x 
> 7
for j := 42 while x > 7, 666 while x > 0, 100, 1 step 1 until 10 do — сначала 
прокрутит некоторое количество раз 42, потом несколько раз 666, потом один раз 
100, потом числа от 1 до 10.

В Алголе-68 тоже был один оператор цикла, который мог быть и for, и while, и 
гибридом. Кстати, вопрос на засыпку: почему в Алголе-68 и Bash нет цикла с 
постусловием (управляющие конструкции Bash слизаны с Алгола-68 почти дословно)?

> Впрочем, вред слишком узкого понимания структурности для императивного 
> программирования был осознан и подробно обоснован еще в начале 1970-х.

Впрочем, и польза от разумной структурности тоже вполне обоснована. Сейчас уже 
является обычной практикой программирование без активного использования goto, 
ряд языков программирования не поддерживают его синтаксически (Java, Python). А 
ведь когда-то goto в программах на высокоуровневых языках встречался столь же 
часто, как и if’ы с циклами — я не говорю про древний Фортран, где иначе 
программировать было нельзя. (Читал как-то отечественную книжку 80-х годов про 
Симулу-67. Редкий метод в ней обходился без goto.)

-----Original Message-----
From: Boyko Bantchev [mailto:boyk...@gmail.com] 
Sent: Wednesday, August 9, 2017 1:58 PM
To: refal@botik.ru
Subject: Re: FW: Рефал умер?

Здравствуйте, Александр!

> Понравилась реализация необязательного параметра n в макросе

Спасибо.  Но этот прием не я придумал.  Видел где-то.

А вот полным решением проблемы с избыточностью имен в struct в Си я горжусь :) 
И в учебниках по программированию на Си, и в реальных программах — тонны этой 
избыточности, но решение показывает, что она не нужна.

> Мой пример выглядел бы так:
>
> int row, col;  // используются после цикла, нельзя объявлять внутри.
> loop (
>   row = 0; col = 0
> , row < MAXROW && elem_at(row, col) == 0 , ++col;
>   if (col == MAXCOL) {
>     col = 0;
>     ++row;
>   }
> )
>   // тело пустое, как я и предполагал
> fin();

Возможно, вы смотрели определение loop в статье?
То, что в файле m.cpp, является более поздним и с ним данный фрагмент
не сработает.   Ваш вариант поиска я записал бы так:

loop (row = 0; col = 0 , row < MAXROW)
exitif (elem_at(row, col) != 0)
upd (if (++col == MAXCOL) { col = 0; ++row;}) fin();

Преимущества:
   проверка нахождения нужного элемента выделена самостоятельно;
   искомое условие для элемента сформулировано положительно;
   тело цикла не пусто;
   обновление индексов лучше сосредоточено в одной конструкции.

> Фишка этого примера в том, что он записывается целиком в рамках структурного 
> программирования: без goto, break и return.

Это если принять, что в структурном программировании их нет.
Но данная точка зрения не единственная.

> Плата за структурное программирование. В варианте Сергея Абрамова (в 
> следующем письме) вводится дополнительная булевская переменная и тоже 
> требуется дополнительная проверка

И, кроме того, дополнительная переменная оказывается нелокальной для цикла,-ов, 
хотя ей лучше быть локальной, ведь дальше она не используется.

Плата за структурное программирование?  Скорее, по-моему, это плата за 
определенное, очень конкретное понимание структурного программирования.  Но 
единого определения этого понятия, с которым все были бы согласны, нет.  Зачем 
подгонять решения задач под какое-то представление о структурности, которое 
даже не является общепризнанным?  Что получаем за «плату»?  Ведь решения 
становятся в той или иной мере искусственными (признаком чего является 
избыточность имен и действий), а что получаем навстречу и так ли оно ценно?  Не 
лучше ли пересмотреть наше понимание о структурности?

Допустим, кто-то постановил, что программировать структурно — это только через 
while и простую последовательность.  Даже if не нужен, он выражается через 
while.  Но такое понимание структурности не берет во внимание ни локальность 
связываний имен, ни количество тех же имен и вообще элементов программ, ни 
обоснованность введения всех этих элементов с точки зрения решаемой задачи (а 
не соблюдения заранее придуманных правил «структурности»).

Признано, что придерживаться какого-нибудь множества структурных схем нужно, но 
по-моему так же нужно выбор этого множества время от времени пересматривать.  
Иначе в императивном программировании мы бы остались с Фортраном или 
Алголом-60, у каждого из которых имеется единственный оператор цикла.  А в 
функциональном программировании так и не пришли бы к лексическому связыванию, 
частичному применению, одноаргументности и композиции функций и сопоставлению с 
образцом — остались бы с Лиспом, у которого их и сегодня нет, за исключением 
лексического связывания.

Впрочем, вред слишком узкого понимания структурности для императивного 
программирования был осознан и подробно обоснован еще в начале 1970-х.

Ответить