Интересно, что никто не упомянул самый, на мой взгляд, большой "оверхэд" (что, русского слова не нашлось? Хотя бы "избыточность") языков в C-образным синтаксисом. Это, конечно, цикл for. Смотрите:
for (int longIndexVariable = 0; longIndexVariable < n; longIndexVariable++)
Сколько лишней писанины! Одно и то же имя приходится писать три раза. И сколько мест для потенциальной ошибки! Вспомните, сколько раз в жизни вы натыкались на ошибку вроде следующей:
for (int i = 0; i < n; i++)
for (int j = 0; j < m; i++)
sum += a[i][j];
Код вроде
for i := 1 to n
for j := 1 to m
sum = sum + a[i][j]
нагляднее выражает, что же мы имели в виду.
И еще к слову. Я все понимаю, я знаю все аргументы в пользу того, чтобы начинать индексацию массивов с 0, а не с 1. И все же я считаю это неестественным!
Здравствуйте, faulx, Вы писали:
F>Сколько лишней писанины! Одно и то же имя приходится писать три раза. И сколько мест для потенциальной ошибки! Вспомните, сколько раз в жизни вы натыкались на ошибку вроде следующей: F>...
Пару раз в самом начале...
F>Код вроде F>... F>нагляднее выражает, что же мы имели в виду.
А использование std::for_each с контейнерами (массивами) делает эту наглядность ещё большей, ещё точнее описывает вышу цель и устраняет ошибки связанные с неверной индексацией... Это так к слову...
F>И еще к слову. Я все понимаю, я знаю все аргументы в пользу того, чтобы начинать индексацию массивов с 0, а не с 1. И все же я считаю это неестественным!
А я считаю неестественным, что одно и то же условие завершения цикла в цикле с предпроверкой записывается как условие, а в цикле с постпроверкой как его отрицание... Извините, но этот момент IMHO более неестественный и сопряжён с гораздо более сложно уловимыми ошибками. Если вы ошибётесь в индексации, есть вероятность, что схлопочите Access violation, а если при преобразовании цикла с предпроверкой в цикл с постпроверкой, вы неверно инвертируете условие, то программа может даже и будет работать... вот только ракета мимо марса пролетит и никто вам об этом не скажет... и никакая защищённая среда Оберона от этого вас не спасёт!
Кстати, если вам кажется неестественной индексация с 0, напишите свой контейнер, где она будет с 1 или с любого указанного вами числа — благо сделать это несложно и этот контейнер будет практически не отличим от стандартных.
Компьютер сделает всё, что вы ему скажете, но это может сильно отличаться от того, что вы имели в виду.
M>>По Дискавери одной из самых сложных программ, работающих сегодня, назвали программу автоматического входа шаттла в атмосферу. С начала входа до -4 км до посадки люди к управлению даже не прикасаются. А там, как никак — управляемое падение (планированием это сложно назвать) на запредельных скоростях (13-25 Мах, что ли).
L>мне кажется программа управляющая взлетом и посадкой Бурана полностью в автоматическом режиме должна быть на порядок сложнее.
L>догадаешся на чем написана ?
Думаю, на ассемблере...
Я отвечаю за свои слова, а не за то как вы их интерпретируете!
MN>А использование std::for_each с контейнерами (массивами) делает эту наглядность ещё большей, ещё точнее описывает вышу цель и устраняет ошибки связанные с неверной индексацией... Это так к слову...
И что теперь, выбросить for из языка? К тому же for_each без лямбд и локальных функций несколько ущербен и требует либо опять-таки писанины, либо библиотек вроде boost. Кстати, как поизящнее выразить суммирование элементов матрицы через стандартные алгоритмы? Только прошу иметь в виду, что выражение вроде a[i][j] (или корректнее в общем случае писать a(i,j)) может быть обращением к вычисляемому элементу. Боюсь, избыточность будет еще больше, чем для варианта с циклами.
MN>А я считаю неестественным, что одно и то же условие завершения цикла в цикле с предпроверкой записывается как условие, а в цикле с постпроверкой как его отрицание... Извините, но этот момент IMHO более неестественный и сопряжён с гораздо более сложно уловимыми ошибками.
При чем здесь предпроверки и постпроверки? Я про них, кажется, ничего не говорил. Конечно, в языках программирования, и не только в них, есть много нееестественного. Но существование одних неестественностей не делает естественными другие неестественности.
MN>Если вы ошибётесь в индексации, есть вероятность, что схлопочите Access violation, а если при преобразовании цикла с предпроверкой в цикл с постпроверкой, вы неверно инвертируете условие, то программа может даже и будет работать... вот только ракета мимо марса пролетит и никто вам об этом не скажет... и никакая защищённая среда Оберона от этого вас не спасёт!
Про Оберон я тоже ничего не говорил, не надо меня записывать в его сторонники. При неверной индексации с ракетой тоже ничего хорошего не произойдет, так что одна ситуация другой стоит.
MN>Кстати, если вам кажется неестественной индексация с 0, напишите свой контейнер, где она будет с 1 или с любого указанного вами числа — благо сделать это несложно и этот контейнер будет практически не отличим от стандартных.
Это само собой, что можно. Написать свой vector, свой string, что там еще? Что там общественность думает по поводу написания собственных контейнеров? В действительности программист стоит перед неприятным выбором — или переписывать кучу кода, или смирится с неестественной индексацией. Выбор разумного программиста известен, но тем печальнее.
F>for i := 1 to n
F> for j := 1 to m
F> sum = sum + a[i][j]
F>
F>нагляднее выражает, что же мы имели в виду.
Нагляднее-то нагляднее, но действия этих циклов неэквивалентны. Скажем как в паскалевский for вместить это:
for (int i = 0; i < n; i += calculate_current_increment())
Я не говорю, что сишный for "правильнее" паскалиного, но приобретая что-то в одном месте — мы неизбежно теряем в другом. Сишный for получил гибкость, потеряв при этом в лаконичности и безопасности. Те, кому это критично — вполне могут юзать итераторы.
К слову: в питоне пошли еще дальше, превратив цикл for в "чистый" итератор, работающий над некоторой коллекцией. То есть там, где в C++ и обероне мы имеем
FOR i := 1 to 10 DO
.....
i := 1; {O-ooops! Совсем забыли, что переменная уже используется в цикле. Вечный цикл однако!}END
в питоне будем иметь
for i in range(10):
...
i = 1 #Переменная i меняется только до конца текущей итерации,
#на следующей это значение будет переписано следующим значением из коллекции range(10)
F>И еще к слову. Я все понимаю, я знаю все аргументы в пользу того, чтобы начинать индексацию массивов с 0, а не с 1. И все же я считаю это неестественным!
F>for i := 1 to n
F> for j := 1 to m
F> sum = sum + a[i][j]
F>
F>нагляднее выражает, что же мы имели в виду.
Но при этом:
1. Переменная цикла должна быть интегрального типа — в C может быть все, что угодно: любая структура, указатель либо вообще может не быть.
2. Она может меняться только на единицу — в С для изменения ты можешь написать любое выражение в том числе с вызовом любых функций.
3. Признаком окончания цикла является достижение переменной заданного значения — в С ты можешь написать любое сколь угодно сложное логическое выражение с вызовом любых функций.
Как видим, гибкость С и дубовость, неповоротливость паскакаля налицо. Кстати, часто программисты, программирующие на С, не используют его возможности, потому что начинали с Паскаля, а потому просто не догадываются, что на С можно то же самое сделать легче и проще. Они остаются морально изуродованы на всю жизнь.
F>И еще к слову. Я все понимаю, я знаю все аргументы в пользу того, чтобы начинать индексацию массивов с 0, а не с 1. И все же я считаю это неестественным!
Для человека может и естественно с единиуы, но для программиста естественно с нуля.
Я отвечаю за свои слова, а не за то как вы их интерпретируете!
Здравствуйте, Пацак, Вы писали:
П>Нагляднее-то нагляднее, но действия этих циклов неэквивалентны. Скажем как в паскалевский for вместить это: П>
П>for (int i = 0; i < n; i += calculate_current_increment())
П>
А вот для понимания подобного "навороченного" for-а приходится вспоминать, что for — это только синтаксический сахар над while и переводить в уме код из for-вида в while-вид. Твой пример — еще один из самых простых, и то, не кажется ли странным, что в коде вида
for (int i = 0; i < n; i += calculate_current_increment())
{
// Много строчек кода
...
...
...
}
функция calculate_current_increment(), помещенная ближе к началу длинного блока кода внутри цикла, вызывается на самом деле в его конце!
Вкратце сказанное можно выразить так: для сложных случаев надо использовать while. Статистических данных у меня нет, но мне кажется, что простые циклы for встречаются гораздо чаще сложных случаев.
П> Я не говорю, что сишный for "правильнее" паскалиного, но приобретая что-то в одном месте — мы неизбежно теряем в другом. Сишный for получил гибкость, потеряв при этом в лаконичности и безопасности. Те, кому это критично — вполне могут юзать итераторы.
Или while. Кстати, какие итераторы в C?
П>К слову: в питоне пошли еще дальше, превратив цикл for в "чистый" итератор, работающий над некоторой коллекцией. То есть там, где в C++ и обероне мы имеем
П>
П>FOR i := 1 to 10 DO
П>.....
П> i := 1; {O-ooops! Совсем забыли, что переменная уже используется в цикле. Вечный цикл однако!}
П>END
П>
Кстати, смутно вспоминаю, что в Паскале внутри цикла нельзя менять переменную цикла. Если это так, то вечного цикла не будет — компилятор не даст.
П>в питоне будем иметь
П>
П>for i in range(10):
П> ...
П> i = 1 #Переменная i меняется только до конца текущей итерации,
П> #на следующей это значение будет переписано следующим значением из коллекции range(10)
П>
А круче всех — лисповский loop.
F>>И еще к слову. Я все понимаю, я знаю все аргументы в пользу того, чтобы начинать индексацию массивов с 0, а не с 1. И все же я считаю это неестественным!
П>Это дело привычки.
Привыкнуть можно к очень многому. Только зачем? Естественно нумеровать вещи с единицы, алгоритмы в книжках (если авторы не испорчены влиянием C) пишутся с нормальной индексацией (с 1), если надо взаимодействовать с пользователем, работать тоже надо с учетом старта с 1. Индексация с 0 — это внутренняя проблема даже не программистов, а скорее компиляторов, и повелась она, по-моему, с C, где была введена для эффективности и для того, чтобы не делать нормальные массивы, а свести их к указателям.
На самом деле, конечно, деваться некуда, и я давно привык. Просто каждый раз раздражает, когда приходится переводить сделанное на бумаге рассуждение с естественной нумерацией в индексацию от нуля. Каждый раз мозги скрипят.
Здравствуйте, qwertyuiop, Вы писали:
Q>Но при этом: Q>1. Переменная цикла должна быть интегрального типа — в C может быть все, что угодно: любая структура, указатель либо вообще может не быть.
Вы сравниваете конкретный язык C с конкретным языком Pascal, а я говорил о синтаксисе. Просто в C-синтаксисе чаще всего приходится писать переменную несколько раз. А в Pascal-синтаксисе только один. Но при этом я не имел в виду конкретно язык Pascal с присущими ему ограничениями, а только синтаксис. Так что можно считать, что переменная цикла может быть не обязательно интегрального типа.
Q>2. Она может меняться только на единицу — в С для изменения ты можешь написать любое выражение в том числе с вызовом любых функций.
Даже в Паскале можно указывать шаг изменения (или это только в Турбо-паскале с потомками?). Но вообще замечание правильное, в Паскаль-синтаксисе места для призвольного изменения переменной цикла практически нет.
Q>3. Признаком окончания цикла является достижение переменной заданного значения — в С ты можешь написать любое сколь угодно сложное логическое выражение с вызовом любых функций.
Опять согласен.
Но! Все это можно прекрасно сделать с помощью цикла while. И зачастую это будет более наглядно. А простой цикл for все же, по-моему, встречается чаще всего. Конечно, в Си Паскалевский for будет неуместен, тем не менее это не отменяет простого факта — в Си в большинстве случаев в цикле for надо писать одно и то же имя переменной три раза.
Q>Как видим, гибкость С и дубовость, неповоротливость паскакаля налицо. Кстати, часто программисты, программирующие на С, не используют его возможности, потому что начинали с Паскаля, а потому просто не догадываются, что на С можно то же самое сделать легче и проще. Они остаются морально изуродованы на всю жизнь.
Интересно, что думают о "гибкости" циклов в Си программисты на Лиспе, где есть вот это?
Q>Для человека может и естественно с единиуы, но для программиста естественно с нуля.
Здравствуйте, faulx, Вы писали:
F>А вот для понимания подобного "навороченного" for-а приходится вспоминать, что for — это только синтаксический сахар над while и переводить в уме код из for-вида в while-вид. Твой пример — еще один из самых простых, и то, не кажется ли странным, что в коде вида
F>
F>for (int i = 0; i < n; i += calculate_current_increment())
F>{
F> // Много строчек кода
F>}
F>
F>функция calculate_current_increment(), помещенная ближе к началу длинного блока кода внутри цикла, вызывается на самом деле в его конце!
Да нет, не особо. Заголовок for'а — это ж по сути декларация того, каковы условия выполнения цикла. Если он собран в одном месте — его можно окинуть одним взглядом, сразу поняв логику работы цикла. Если будет разнесен часть в начало часть в конец — придется бегать глазами по коду, это не так удобно.
F>Или while. Кстати, какие итераторы в C?
Кто запрещает юзать С++? Крайние случаи в виде экзотических платформ в расчет не берем — паскалей там тоже, как правило, не ночевало. А while — ну да, можно, но потеряется наглядность. А если еще и break внутри цикла надо сделать — так в обероне вообще до LOOP докатимся, как выяснилось. Можно конечно это рассматривать как идеологически правильный мегарулез, но как-то не воодушевляет...
F>Кстати, смутно вспоминаю, что в Паскале внутри цикла нельзя менять переменную цикла. Если это так, то вечного цикла не будет — компилятор не даст.
Совсем не уверен, но проверить не могу — ни паскаля, ни оберона под рукой нет.
F>А круче всех — лисповский loop.
Ну видишь — везде решают по-своему, исходя из того, что было необходимо. Разработчикам С был нужен универсальный инструмент, в котором можно лаконично описать достаточно сложные условия выполнения итераций, они себе сделали вот такой вот for. С паскалиным он имеет мало общего, сравнивать их — довольно некорректное занятие.
F> в Си в большинстве случаев в цикле for надо писать одно и то же имя переменной три раза.
Так я же говорю: ты научился думать на паскале, поэтому, переводя свои мысли на С тебе приходится писать имя переменной три раза. А если ты научишься думать на С, то возможно переменная цикла тебе и не понадобится:
Здравствуйте, Пацак, Вы писали:
F>>функция calculate_current_increment(), помещенная ближе к началу длинного блока кода внутри цикла, вызывается на самом деле в его конце!
П>Да нет, не особо. Заголовок for'а — это ж по сути декларация того, каковы условия выполнения цикла. Если он собран в одном месте — его можно окинуть одним взглядом, сразу поняв логику работы цикла. Если будет разнесен часть в начало часть в конец — придется бегать глазами по коду, это не так удобно.
А по-моему, удобно, когда вызов функции записывается там, где он действительно производится. А то можно было бы ввести такую конструкцию (это я пишу с учетом форума, в котором мы находимся):
Такой блок — тоже, по сути, декларация того, что мы будем делать в блоке. Сразу видно, чем все закончится. По коду бегать глазами не надо.
Кстати, если задуматься, этот пример:
F>for (int i = 0; i < n; i += calculate_current_increment())
F>{
F> // Много строчек кода
F>}
довольно абстрактный. Ведь, наверное, calculate_current_increment() вызывается не просто так, а туда передаются какие-то параметры. И скорее всего они возникают где-то в середине блока цикла. И писать приходится как-то так:
for (int i = 0; i < n; )
{
// Много строчек кодаint j = ...
i += calculate_current_increment(j);
}
П>Кто запрещает юзать С++? Крайние случаи в виде экзотических платформ в расчет не берем — паскалей там тоже, как правило, не ночевало.
Речь не про конкретный язык, а про синтаксис, а он у циклов в C и C++ одинаковый. А поскольку, несмотря на существование итераторов и стандартных алгоритмов, цикл for в C++ существует и, по-видимому, широко используется, проблема его синтаксической избыточности (долой слово "оверхэд"!) остается.
П>А while — ну да, можно, но потеряется наглядность. А если еще и break внутри цикла надо сделать — так в обероне вообще до LOOP докатимся, как выяснилось. Можно конечно это рассматривать как идеологически правильный мегарулез, но как-то не воодушевляет...
Про Оберон я не говорил.
F>>Кстати, смутно вспоминаю, что в Паскале внутри цикла нельзя менять переменную цикла. Если это так, то вечного цикла не будет — компилятор не даст.
П>Совсем не уверен, но проверить не могу — ни паскаля, ни оберона под рукой нет.
Посмотрел в интернете, кажется, Паскаль действительно не запрещает. Позор! Интересно, как с этим в Аде и Обероне?
F>>А круче всех — лисповский loop.
П>Ну видишь — везде решают по-своему, исходя из того, что было необходимо. Разработчикам С был нужен универсальный инструмент, в котором можно лаконично описать достаточно сложные условия выполнения итераций, они себе сделали вот такой вот for. С паскалиным он имеет мало общего, сравнивать их — довольно некорректное занятие.
Смотря по каким критериям. Можно долго философствовать на тему корректности и некорректности сравнений, но факт остается фактом — для простых циклов типа for в Паскалевском синтаксисе надо писать имя переменной один раз, а в Сишном — три. Достичь гибкости Си и экономности Паскаля в этом конкретном вопросе — вполне в человеческих силах, чему подтверждение Лисповский loop.
F>довольно абстрактный. Ведь, наверное, calculate_current_increment() вызывается не просто так, а туда передаются какие-то параметры. И скорее всего они возникают где-то в середине блока цикла. И писать приходится как-то так:
F>
F>for (int i = 0; i < n; )
F>{
F> // Много строчек кода
F> int j = ...
F> i += calculate_current_increment(j);
F>}
F>
А как быть, если цикл выглядит примерно так:
for (int i = 0; i < n; )
{
// Много строчек кодаint j = ...
i += calculate_current_increment(j);
continue;
// Еще много строчек кода
j = ...
i += calculate_current_increment(j);
continue;
// И здесь много строчек кода
j = ...
i += calculate_current_increment(j);
continue;
// ...
}
И так перед каждым continue?
Я отвечаю за свои слова, а не за то как вы их интерпретируете!
Здравствуйте, AVC, Вы писали:
AVC>а) Соответствие структурным принципам.
AVC>В отличие от синтаксиса Модулы/Оберона синтаксис Си/Си++ не соответствует принципам структурного программирования. AVC>Рассмотрим хотя бы циклы.
Вобщем, на основании этой ветки и доп. источников я вывел:
1. Циклы С/С++ не соответствуют принципам структурного программирования. Т.к. могут иметь более 1 выхода.
2. Цикл LOOP оберона так-же не соответствуют принципам структурного программирования. Причина аналогичная.
Вывод: оба языка не соответствует принципам структурного программирования.
3. Вот что я случайно вычитал в "Рефакторинг. Улучшение существующего кода", М. Фаулер:
Глава 9.
Удаление управляющего флага (Remove Control Flag)
...
От таких управляющих флагов больше неприятностей, чем пользы. Их присутствие диктуется правилами структурного программирования, согасно которым в процедурах должна быть одна точка выхода (чего требуют современные языки программирования), но требование одной точки выхода приводит к сильно запутанным условным операторам, в коде которых есть такие неудобные флаги. Для того чтобы выбраться из сложного условного опертора, в я зыках ксть команды break и continue. Часто бывает удивительно, чего можно достичь, избавившись от управляющего флага. Дкйствительно назначение условного оператора становится гораздо понятнее.
Т.о. я пришёл к выводу, что Обероновский синтаксис мало того, что не соответствует принципам структурного программирования, так ещё и ставит палки в колёса, запрещая более полезные, чем вредные break и continue.
Здравствуйте, qwertyuiop, Вы писали:
Q>Так я же говорю: ты научился думать на паскале, поэтому, переводя свои мысли на С тебе приходится писать имя переменной три раза. А если ты научишься думать на С, то возможно переменная цикла тебе и не понадобится: Q>
Переменная GeoProxy пишется два раза (и, кажется, один раз пропущен). Разницы нет.
Q>Программист — он в первую очередь математик, а в математике первое число — 0!
Математик — тоже человек. Понятия "первого числа" в математике нет. Само слово "первый" подразумевает единицу.
Здравствуйте, qwertyuiop, Вы писали:
Q>А как быть, если цикл выглядит примерно так: Q>
Q>for (int i = 0; i < n; )
Q>{
Q> // Много строчек кода
Q> int j = ...
Q> i += calculate_current_increment(j);
Q> continue;
Q> // Еще много строчек кода
Q> j = ...
Q> i += calculate_current_increment(j);
Q> continue;
Q> // И здесь много строчек кода
Q> j = ...
Q> i += calculate_current_increment(j);
Q> continue;
Q> // ...
Q>}
Q>
Q>И так перед каждым continue?
int i = 0; // Пока забудем про область видимости iwhile (i < n)
{
// Много строчек кодаint j = ...
i += calculate_current_increment(j);
continue;
// Еще много строчек кода [q]на которые мы никогда не попадем, если не используем goto[/q]
j = ...
i += calculate_current_increment(j);
continue;
// И здесь много строчек кода [q]на которые мы никогда не попадем, если не используем еще один goto[/q]
j = ...
i += calculate_current_increment(j);
continue;
// ...
}
Ниасилил!
Сергей, все это неимоверно круто, но тема е... ну ты понял. Не раскрыта в общем.
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re[8]: Синтаксический оверхед
От:
Аноним
Дата:
24.06.05 06:26
Оценка:
Q>>И так перед каждым continue?
F>
F>int i = 0; // Пока забудем про область видимости i
F>while (i < n)
F>{
F> // Много строчек кода
F> int j = ...
F> i += calculate_current_increment(j);
F> continue;
F> // Еще много строчек кода [q]на которые мы никогда не попадем, если не используем goto[/q]
F>
Опять придираемся? Или ты доказываешь, что оператор continue не нужен?
Здравствуйте, Аноним, Вы писали:
Q>>>И так перед каждым continue?
F>>
F>>int i = 0; // Пока забудем про область видимости i
F>>while (i < n)
F>>{
F>> // Много строчек кода
F>> int j = ...
F>> i += calculate_current_increment(j);
F>> continue;
F>> // Еще много строчек кода [q]на которые мы никогда не попадем, если не используем goto[/q]
F>>
А>Опять придираемся? Или ты доказываешь, что оператор continue не нужен?
Да я понял, что имелось в виду, поэтому не стал удалять код и все-таки расписал в while.
А>