Здравствуйте, Бойко! > А вот полным решением проблемы с избыточностью имен в 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-х.