Comments 46
Ждём статью «goto в спортивном программировании» :)
спортивное как раз хорошо попадает под прикладное:
там важна общая реализация алгоритма,
а ограничения по ресурсам ставятся такие, что их выполнение/невыполнение зависит только от алгоритма, а не конкретных инструкций в его реализации.
там важна общая реализация алгоритма,
а ограничения по ресурсам ставятся такие, что их выполнение/невыполнение зависит только от алгоритма, а не конкретных инструкций в его реализации.
Не скажите — иногда один и тот же код с vector'ами не проходит, а с массивами получает AC.
Хотя goto, разумеется, таких «оптимизаций» не даст.
Хотя goto, разумеется, таких «оптимизаций» не даст.
Чаще всего просто не нужно (зато break и continue юзается десятками раз в каждом исходнике).
Единственный пресловутый случай, когда мне goto на олимпиаде помогало — это тот самый выход из нескольких циклов.
Единственный пресловутый случай, когда мне goto на олимпиаде помогало — это тот самый выход из нескольких циклов.
Задачка:
Есть страшный расчет, который надо проводить тысячи раз. В каждом есть10 подзадач, каждая занимает по пол часа. (Реально есть)
В качестве вычислителя есть только ноутбук 2 ядерных по 1.6ггц. Ни каких других вычислительных мощьностей ближайшие пару месяцев не предвидится, а посчитать надо.
Держать ноутбук не выключая более 2 часов не возможно.
Внутри есть 3 адских цикла, каждый из которых не параллелиться тк имеет зависимость по данным вполне себе прямую.
Необходимо реализовать возможность остановки расчета, выключения компьютера и при выключении начать с того же места.
Варианты типа мельчить на задачи- уже использован, все равно 3 часа, меньше не получается, зато теперь есть сотни папок с результатам, Которые стало тяжело анализировать.
Скажите лучшее решение, чем без goto, тк мне самому оно не нравится ибо велосипед.
Я серьезно!
Мое решение, но пока оно только сделано не используется.
Я реализовал паттерн- моменто(я бы назвал его скорее дампом).
При поступлении события, все память складывается в xml, вместе со всеми значениями счетчиков циклов и временными переменными. Затем, чтобы возобновить процесс вызывается точная копия метода, где происходил процесс с несколькими отличиями, во первых он получает в качестве параметра номер метки goto на которую надо перейти, чтобы начать процесс с той же точки, что и он был прерван. Далее идет инициализация всех переменных из дампа, и через маленький switch по тому номеру метки идем в нужное место циклов. В итоге расчет начинается с той же точки на которой он был прерван.
Чем плохо- есть полный дубляж счетного метода, есть goto.
Какое лучшее решении можно сделать? Я на полном серьезе спрашиваю, если кто предложит ответ- буду рад как кот сметане.
Язык C#(переписывать на С++ не предлагать, это на долго и в отладке я погрязну еще на долго.)
Есть страшный расчет, который надо проводить тысячи раз. В каждом есть10 подзадач, каждая занимает по пол часа. (Реально есть)
В качестве вычислителя есть только ноутбук 2 ядерных по 1.6ггц. Ни каких других вычислительных мощьностей ближайшие пару месяцев не предвидится, а посчитать надо.
Держать ноутбук не выключая более 2 часов не возможно.
Внутри есть 3 адских цикла, каждый из которых не параллелиться тк имеет зависимость по данным вполне себе прямую.
Необходимо реализовать возможность остановки расчета, выключения компьютера и при выключении начать с того же места.
Варианты типа мельчить на задачи- уже использован, все равно 3 часа, меньше не получается, зато теперь есть сотни папок с результатам, Которые стало тяжело анализировать.
Скажите лучшее решение, чем без goto, тк мне самому оно не нравится ибо велосипед.
Я серьезно!
Мое решение, но пока оно только сделано не используется.
Я реализовал паттерн- моменто(я бы назвал его скорее дампом).
При поступлении события, все память складывается в xml, вместе со всеми значениями счетчиков циклов и временными переменными. Затем, чтобы возобновить процесс вызывается точная копия метода, где происходил процесс с несколькими отличиями, во первых он получает в качестве параметра номер метки goto на которую надо перейти, чтобы начать процесс с той же точки, что и он был прерван. Далее идет инициализация всех переменных из дампа, и через маленький switch по тому номеру метки идем в нужное место циклов. В итоге расчет начинается с той же точки на которой он был прерван.
Чем плохо- есть полный дубляж счетного метода, есть goto.
Какое лучшее решении можно сделать? Я на полном серьезе спрашиваю, если кто предложит ответ- буду рад как кот сметане.
Язык C#(переписывать на С++ не предлагать, это на долго и в отладке я погрязну еще на долго.)
Действительно страшно. На какой глубине находится самая глубокая точка сохранения?
Можно предложить:
— реализовать методы, которые будут выполнять завершение внутренних циклов, возвращаться и передавать управление методам для более внешних циклов (плохо, потому что код умножается во много раз)
— пропускать уже вычисленные части циклов с помощью if() (которые будут срабатывать только при первом входе)
— описать состояние процесса в самой глубокой точке сохранения и реализовать всю программу как один цикл, реализующий переход от такого состояния к следующему.
Я бы, наверное, стал делать так. Тело каждого цикла выделил бы в функцию, которая что-то бы делала, а дойдя до внутреннего цикла, вызывала бы в нем функцию следующего уровня. У функции — параметры, позволяющие описать досрочный выход и продолжение счета (блок с копией локальных переменных?). Но это тоже громоздко.
Можно предложить:
— реализовать методы, которые будут выполнять завершение внутренних циклов, возвращаться и передавать управление методам для более внешних циклов (плохо, потому что код умножается во много раз)
— пропускать уже вычисленные части циклов с помощью if() (которые будут срабатывать только при первом входе)
— описать состояние процесса в самой глубокой точке сохранения и реализовать всю программу как один цикл, реализующий переход от такого состояния к следующему.
Я бы, наверное, стал делать так. Тело каждого цикла выделил бы в функцию, которая что-то бы делала, а дойдя до внутреннего цикла, вызывала бы в нем функцию следующего уровня. У функции — параметры, позволяющие описать досрочный выход и продолжение счета (блок с копией локальных переменных?). Но это тоже громоздко.
Вот ваш вариант мне вполне себе нравится, но тогда придется либо кучу(порядка 15, не хочу вызовы превращать в winapi стиль) параметров передавать в функцию и все по ссылку ref, либо на плодить глобальных переменных.
Реализовывать все как 1 цикл- я вас не понял.
Вариант с if действительно выглядит элегантно относительно.
Получится что есть 3 переменных булевских, которые говорят работал ли цикл в момент отключения или нет. И потом if(var1) то заходим в блок. далее if(var2) заходим, а в последнем if(var3) оказывается false, значит с этой точки и считаем. Соответственно при выходе из цикла переменную ему соответствующую ставим в false, что мы от туда вышли.
Надо попробовать сделать.
Реализовывать все как 1 цикл- я вас не понял.
Вариант с if действительно выглядит элегантно относительно.
Получится что есть 3 переменных булевских, которые говорят работал ли цикл в момент отключения или нет. И потом if(var1) то заходим в блок. далее if(var2) заходим, а в последнем if(var3) оказывается false, значит с этой точки и считаем. Соответственно при выходе из цикла переменную ему соответствующую ставим в false, что мы от туда вышли.
Надо попробовать сделать.
Думаю, что сохраненные переменные лучше организовать в классы, причем структура классов должна отражать структуру циклов: цикл на каком-то уровне получает инициализирующую переменную, если она null — значит это штатный вход, а если нет — сохраненный. Извлекает из нее объект для цикла следующего уровня. Тот тоже смотрит: если это не null — значит, это его сохраненные данные, а если null — сохраненных данных не было, вход штатный. По-моему, должно сложиться. Плохо, если точки входа образуют не линейный список, а дерево. Но и тогда можно разобраться.
Надо бы что-то типа этого: ru.wikipedia.org/wiki/Setcontext
Но тут про C. Может аналог есть.
Но тут про C. Может аналог есть.
Вариант хороший, но я не нашел ничего подобного для C#
Как то у одного товарища моего знакомого было желание взять весь thread(а лучше объект обертка над ним из .net 4) остановить его и в файл сохранить… Чтобы потом восстановить.
По моему у него это не удалось ибо это все таки не системное программирование и вроде на C# средств таких нет.
Как то у одного товарища моего знакомого было желание взять весь thread(а лучше объект обертка над ним из .net 4) остановить его и в файл сохранить… Чтобы потом восстановить.
По моему у него это не удалось ибо это все таки не системное программирование и вроде на C# средств таких нет.
в аналогичных случаях, правда, гораздо более примитивных, поступал так:
Поток обработки UI при нажатии кнопки «пауза» просто поднимал флаг «пауза». Рабочий поток проверял этот флаг в фиксированном месте, в котором легко и удобно можно было все задампить, и так же легко можно было позднее восстановить состояние и продолжить работу — это было в конце оборота глобального цикла, когда стек минимален, все промежуточные расчеты получены.
если у Вас вложенные циклы сами по себе долго выполняются, наверное, придется сделать сложнее: так же один флаг, но проверять его на нескольких уровнях вложенности, на каждом сохранять свой кусочек стека.
Поток обработки UI при нажатии кнопки «пауза» просто поднимал флаг «пауза». Рабочий поток проверял этот флаг в фиксированном месте, в котором легко и удобно можно было все задампить, и так же легко можно было позднее восстановить состояние и продолжить работу — это было в конце оборота глобального цикла, когда стек минимален, все промежуточные расчеты получены.
если у Вас вложенные циклы сами по себе долго выполняются, наверное, придется сделать сложнее: так же один флаг, но проверять его на нескольких уровнях вложенности, на каждом сохранять свой кусочек стека.
Самый низкий цикл то как раз не очень долго выполняется, потеря пары минут- не критична…
Наверное так и следует делать, сохранять только в допустим самом верхнем из 3 циклов.
Но вот потеря второго цикла- это уже пол часа- а это плохо. Можно избавиться от 1 цикла и 1 goto соответственно, но еще 1 получится останется.
Полумера выходит. Избавился от 2 строк, но добавлю проверку на флаг+ ожидание выхода из нижнего цикла.
Если бы понять как во второй цикл войти и выйти без goto, то я бы так как вы сказали и сделал
Наверное так и следует делать, сохранять только в допустим самом верхнем из 3 циклов.
Но вот потеря второго цикла- это уже пол часа- а это плохо. Можно избавиться от 1 цикла и 1 goto соответственно, но еще 1 получится останется.
Полумера выходит. Избавился от 2 строк, но добавлю проверку на флаг+ ожидание выхода из нижнего цикла.
Если бы понять как во второй цикл войти и выйти без goto, то я бы так как вы сказали и сделал
А если попробовать «развернуть» 3 цикла в один:
int L1 = 0;
int L2 = 0;
int L3 = 0;
while (L3 < 2){
Console.WriteLine(«L1:{0} L2:{1} L3:{2}», L1, L2, L3);
L1++;
if (L1 == 2){
L1 = 0;
L2++;
}
if (L2 == 2){
L2 = 0;
L3++;
}
}
Оформляем этот кусок кода в виде функции, которой в параметрах передаются L1, L2, L3 и вход в нужное состояние осуществляется без GOTO.
int L1 = 0;
int L2 = 0;
int L3 = 0;
while (L3 < 2){
Console.WriteLine(«L1:{0} L2:{1} L3:{2}», L1, L2, L3);
L1++;
if (L1 == 2){
L1 = 0;
L2++;
}
if (L2 == 2){
L2 = 0;
L3++;
}
}
Оформляем этот кусок кода в виде функции, которой в параметрах передаются L1, L2, L3 и вход в нужное состояние осуществляется без GOTO.
«1. нет необходимости в точечных оптимизациях — отдельных тактов или ячеек памяти, поэтому экономию ресурсов можно отбросить из рассмотрения GOTO — остается только сопровождаемость»
Не совсем понятно, почему это так. Во внутреннем цикле обработки большого объема данных, который выполняется 10^9 и более раз, каждые несколько тактов — это лишняя секунда времени работы. И заставлять процессор работать с флагами там, где достаточно обойтись явно прописанным переходом, может оказаться не очень разумно. Лишняя переменная — это не просто ячейка памяти, это еще и регистр процессора. А их запас ограничен.
Не совсем понятно, почему это так. Во внутреннем цикле обработки большого объема данных, который выполняется 10^9 и более раз, каждые несколько тактов — это лишняя секунда времени работы. И заставлять процессор работать с флагами там, где достаточно обойтись явно прописанным переходом, может оказаться не очень разумно. Лишняя переменная — это не просто ячейка памяти, это еще и регистр процессора. А их запас ограничен.
Хорошее замечание.
В таком случае нужно:
1) определить, насколько высокоуровневый код используется в этом цикле: если код выполняется на заведомо высокоуровневом языке — C#, Java, C++ с использованием продвинутого ООП (наследование, шаблоны), интерпретируемом языке и т.п., — то goto Вас не спасет: уже есть много накладных расходов, на фоне которых goto теряется.
2) если все-таки хотите попробовать оптимизировать, плясать не от пристального взгляда на исходники, а от показаний профайлера — какие узкие точки показывает он (хотя, возможно, на таких масштабах оптимизации профайлер начинает искажать показания за счет изменения исходного кода)
3) если все-таки взялись оптимизировать, и при этом попадаете в тему этой статьи — занимаетесь прикладным программированием на высокоуровневом языке — нет смысла париться о таких правилах: это значит, вы уже отказались от всех высокоуровневых концепций, и спустились на уровень ассемблера/Си, чтобы быть в состоянии контролировать такую точность оптимизации.
В общем, максимальная оптимизация всегда усложняет код, нарушая кучу рекомендаций — никуда от этого не деться, но главное — 7 раз проверить, что это имеет смысл
В таком случае нужно:
1) определить, насколько высокоуровневый код используется в этом цикле: если код выполняется на заведомо высокоуровневом языке — C#, Java, C++ с использованием продвинутого ООП (наследование, шаблоны), интерпретируемом языке и т.п., — то goto Вас не спасет: уже есть много накладных расходов, на фоне которых goto теряется.
2) если все-таки хотите попробовать оптимизировать, плясать не от пристального взгляда на исходники, а от показаний профайлера — какие узкие точки показывает он (хотя, возможно, на таких масштабах оптимизации профайлер начинает искажать показания за счет изменения исходного кода)
3) если все-таки взялись оптимизировать, и при этом попадаете в тему этой статьи — занимаетесь прикладным программированием на высокоуровневом языке — нет смысла париться о таких правилах: это значит, вы уже отказались от всех высокоуровневых концепций, и спустились на уровень ассемблера/Си, чтобы быть в состоянии контролировать такую точность оптимизации.
В общем, максимальная оптимизация всегда усложняет код, нарушая кучу рекомендаций — никуда от этого не деться, но главное — 7 раз проверить, что это имеет смысл
Со всем согласен, кроме «продвинутого ООП (наследование, шаблоны)» — во-первых шаблоны вообще не очень-то ООП, а во-вторых они сами по себе не делают код медленнее, а зачастую используются как раз для достижения обратного эффекта. Кроме того, наследование тоже само по себе ничем не грозит, т.к. это просто расширение структуры данных (плюс мизерный оверхед от конструктора-деструктора которые можно и заинлайнить). В любом случае, нано-оптимизации о которых идёт речь в случае с goto могут иметь измеримый эффект только в очень нагруженном цикле, и то не факт, что он там будет. Во всех остальных случаях имхо нужно писать так, как этого ожидают другие люди которые будут потенциально работать с этим кодом.
UFO just landed and posted this here
UFO just landed and posted this here
да, я неспроста описал свойство прикладного программирования «возможность обработки ошибок через исключения». наверное, недостаточно четко описал, поэтому подредактировал начало темы, добавив определение прикладного программирования в данном контексте. Язык C не входит в это определение — более низкоуровневый.
Да в C++ механизм обработки исключений тоже не идеален. Нет блока try/catch/finally — попробуй сделай нормально финализацию. А блок try/catch не перехватывает Win32-исключений (например, ошибка деления на ноль). В свою очерень, SEX-блок __try/__finally может не перехватывать исключения C++.
UFO just landed and posted this here
На самом деле достаточно было написать
«В прикладном программировании критически важен один параметр кода — сопровождаемость.»
И заставить всех программистов в мире это выучить. А потом убедиться, что поняли.
Остальное — следствия.
«В прикладном программировании критически важен один параметр кода — сопровождаемость.»
И заставить всех программистов в мире это выучить. А потом убедиться, что поняли.
Остальное — следствия.
Да, было бы достаточно :)
Только мы же люди аналитические — набиваем шишки, анализируем, выводим общие правила, потом дзен, а потом пытаемся заставить других этот дзен понять быстрее. Но заставление плохо проходит даже с теми, кто рядом. Хотя рычаги всегда есть — сводятся к «заставлению» поддерживать свой код.
Только мы же люди аналитические — набиваем шишки, анализируем, выводим общие правила, потом дзен, а потом пытаемся заставить других этот дзен понять быстрее. Но заставление плохо проходит даже с теми, кто рядом. Хотя рычаги всегда есть — сводятся к «заставлению» поддерживать свой код.
Автор делает одно очень важное допущение — он считает что все используют C++.
В C есть конечно longjmp, но лишний раз его использовать тот ещё аттракцион.
В C есть конечно longjmp, но лишний раз его использовать тот ещё аттракцион.
Я явно написал, что это общее для всех высокоуровневых языков, поддерживающих goto — C#, Java, PHP,…
MSDN: msdn.microsoft.com/ru-ru/library/13940fs2#Y240
Кратко: в C# применение оператора goto оправдано для:
1) Перехода между case-метками внутри switch-блока;
2) Для выхода из вложенных циклов.
p.s. Иначе такие примеры не приводились бы в официальных руководствах, причём без всякого рода предостережений, верно?
Кратко: в C# применение оператора goto оправдано для:
1) Перехода между case-метками внутри switch-блока;
2) Для выхода из вложенных циклов.
p.s. Иначе такие примеры не приводились бы в официальных руководствах, причём без всякого рода предостережений, верно?
Конечно, читал это во время написания (только английский вариант — этот русский перевод удивил своим «качеством»).
К сожалению, примеры насквозь кривые.
Конкретно по примерам:
1. switch — хоть и маленький, а уже непонятный пример (понять можно, но нужно включить голову и внимательность):
1) сначала хотел написать альтернативу — вешать флаг внутри switch, затем в соответствии с флагом выполнять. Но данный извращенный код вообще из пальца высосан, таких авторов надо увольнять, а код заменять на примитивный:
2) за magic constants 25 и 50 уже можно руки оторвать любому, независимо от фирмы. А Вы говорите «MSDN», эх(((
2. вложенный цикл: 1) создает сразу 2 метки; 2) дублирование — несколько раз вызывается Console.Writeline — как раз потому, что вместо отслеживания результата поиска применили goto.
А так было бы:
К сожалению, примеры насквозь кривые.
Конкретно по примерам:
1. switch — хоть и маленький, а уже непонятный пример (понять можно, но нужно включить голову и внимательность):
1) сначала хотел написать альтернативу — вешать флаг внутри switch, затем в соответствии с флагом выполнять. Но данный извращенный код вообще из пальца высосан, таких авторов надо увольнять, а код заменять на примитивный:
Hashtable pricesBySizes = new Hashtable();
pricesBySizes[smallSize] = smallSizePrice;
...
if(pricesBySizes.ContainsKey(size)
//запишем цену
else
//отругаем за неверный ввод
2) за magic constants 25 и 50 уже можно руки оторвать любому, независимо от фирмы. А Вы говорите «MSDN», эх(((
2. вложенный цикл: 1) создает сразу 2 метки; 2) дублирование — несколько раз вызывается Console.Writeline — как раз потому, что вместо отслеживания результата поиска применили goto.
А так было бы:
result = findEqualNumber(array, myNumber);
resultString = "The number {0} " + (result == null ? "was not" : "is") + "found";
//вывести результат
Это автоматический перевод на русский. Примеры, как можно понять, демонстративные — демонстрируют, какой синтаксис нужно использовать для goto внутри того же switch — и я не вижу смысла где-то зараннее объявлять числа, чтобы они перестали быть волшебными, а пример от этого стал длиннее.
Может, авторитетным для вам окажется мнение 9 специалистов (Симон Робинсон, Олли Корнес...), написавших «C# для профессионалов»: «Как правило, не рекомендуется использовать оператор goto. Вообще говоря, его применение противоречит объектно-ориентированному стилю программирования. Однако в одном случае этот оператор очень удобен — в случае переходов между метками в операторе switch, в частности потому, что оператор switch в C# не позволяет „проваливаться“ сквозь метки case.»
И да, никого не надо увольнять.
Может, авторитетным для вам окажется мнение 9 специалистов (Симон Робинсон, Олли Корнес...), написавших «C# для профессионалов»: «Как правило, не рекомендуется использовать оператор goto. Вообще говоря, его применение противоречит объектно-ориентированному стилю программирования. Однако в одном случае этот оператор очень удобен — в случае переходов между метками в операторе switch, в частности потому, что оператор switch в C# не позволяет „проваливаться“ сквозь метки case.»
И да, никого не надо увольнять.
Тоже подозреваю, что автоматический перевод, но при этом прямо написано — «переведен вручную».
Согласен с Вами — примеры демонстрируют именно синтаксис и семантику goto. Но никак не образец написания кода.
Безотносительно отношения к авторитетам рассмотрим приведенное Вами высказывание:
1. Не рекомендуется. 2. Противоречит ООП. 3. Удобен в switch. 4. в частности, чтобы проваливаться сквозь метки case:
3. удобен — я это понимаю как «удобен в написании»: если имеем индульгенцию использовать его в switch, то уже не будем париться, думать, как сделать по-другому — возьмем и напишем;
4. «проваливаться сквозь case»:
1) заметьте, пример MSDN даже сюда не подходит: в нем прыгают наверх — возможно, разница тонкая, но есть;
2) разве хорошо проваливаться сквозь метки только потому, что в С/С++ это было, и мы привыкли? Нет, это ошибкоопасно: сложнее заметить, где есть break, где нет — для этого и сделали обязательным break. Это, если хотите, синтаксическая соль, а мы хотим ее обойти.
Код должен быть скучной инструкцией, а не детективом — пусть он будет длиннее в сумме за счет большего количества объявлений, но каждый маленький участок будет максимально прост и линеен.
Согласен с Вами — примеры демонстрируют именно синтаксис и семантику goto. Но никак не образец написания кода.
Безотносительно отношения к авторитетам рассмотрим приведенное Вами высказывание:
1. Не рекомендуется. 2. Противоречит ООП. 3. Удобен в switch. 4. в частности, чтобы проваливаться сквозь метки case:
3. удобен — я это понимаю как «удобен в написании»: если имеем индульгенцию использовать его в switch, то уже не будем париться, думать, как сделать по-другому — возьмем и напишем;
4. «проваливаться сквозь case»:
1) заметьте, пример MSDN даже сюда не подходит: в нем прыгают наверх — возможно, разница тонкая, но есть;
2) разве хорошо проваливаться сквозь метки только потому, что в С/С++ это было, и мы привыкли? Нет, это ошибкоопасно: сложнее заметить, где есть break, где нет — для этого и сделали обязательным break. Это, если хотите, синтаксическая соль, а мы хотим ее обойти.
Код должен быть скучной инструкцией, а не детективом — пусть он будет длиннее в сумме за счет большего количества объявлений, но каждый маленький участок будет максимально прост и линеен.
Да, и меня смущает всё время это «переведен вручную». Но зачастую там откровенные моменты есть, которые… ну никак не мог бы допустить человек с минимальным знанием английского.
Едем дальше:
1) «Не рекомендуется» — это да. Но обратите внимание, как осторожны здесь авторы. Они говорят «Как правило, не рекомендуется» (<=> «Обычно, не рекомендуется»). Это значит, что есть исключения, и именно в конкретных исключительных ситуациях (блок switch, выход из вложенных циклов) goto оправдан и является одним из верных решений.
2) «Удобен в switch» — удобен и в написании (не нужно объявлять меток) и в использовании, но только в ситуациях, когда он там требуется.
3) "… в частности, чтобы проваливаться сквозь метки case" — «разве хорошо проваливаться сквозь метки только потому, что в С/С++ это было, и мы привыкли?» — именно поэтому в C# теперь нельзя проваливаться сквозь case — разработчики языка пытались максимально уменьшить число ошибок, допускаемых программистом («после case cтавь break»), но при этом сохранить гибкость С++ («юзай лучше goto, когда надо повторно использовать код внутри switch и выделять для этого отдельную функцию малоприемлемо»).
4) «Код должен быть скучной инструкцией, а не детективом» — если имеется ввиду, что goto «перебрасывает» через код, то вызов функции — это тоже «скачёк» к другому участку, в конце while, for тоже скачёк к началу цикла.
… У меня вёл преподаватель, который категорически запрещал goto, break (кроме как в switch), continue и несколько return-ов. Просто сказал бы «переписывай», если бы я заюзал «сами знаете что». Попытавшись с ним поспорить однажды нарвался на некоторую грубость и нежелание слушать. Не то, чтобы «запретный плод сладок», но в процессе своего самообразования я понимал, что это скорее предвзятость и «старая школа», а новая учит использовать весь доступный в языке инструментарий (иначе зачем его туда ввели разработчики языка — думаю первые, кто знает, что делают), но с умом.
Едем дальше:
1) «Не рекомендуется» — это да. Но обратите внимание, как осторожны здесь авторы. Они говорят «Как правило, не рекомендуется» (<=> «Обычно, не рекомендуется»). Это значит, что есть исключения, и именно в конкретных исключительных ситуациях (блок switch, выход из вложенных циклов) goto оправдан и является одним из верных решений.
2) «Удобен в switch» — удобен и в написании (не нужно объявлять меток) и в использовании, но только в ситуациях, когда он там требуется.
3) "… в частности, чтобы проваливаться сквозь метки case" — «разве хорошо проваливаться сквозь метки только потому, что в С/С++ это было, и мы привыкли?» — именно поэтому в C# теперь нельзя проваливаться сквозь case — разработчики языка пытались максимально уменьшить число ошибок, допускаемых программистом («после case cтавь break»), но при этом сохранить гибкость С++ («юзай лучше goto, когда надо повторно использовать код внутри switch и выделять для этого отдельную функцию малоприемлемо»).
4) «Код должен быть скучной инструкцией, а не детективом» — если имеется ввиду, что goto «перебрасывает» через код, то вызов функции — это тоже «скачёк» к другому участку, в конце while, for тоже скачёк к началу цикла.
… У меня вёл преподаватель, который категорически запрещал goto, break (кроме как в switch), continue и несколько return-ов. Просто сказал бы «переписывай», если бы я заюзал «сами знаете что». Попытавшись с ним поспорить однажды нарвался на некоторую грубость и нежелание слушать. Не то, чтобы «запретный плод сладок», но в процессе своего самообразования я понимал, что это скорее предвзятость и «старая школа», а новая учит использовать весь доступный в языке инструментарий (иначе зачем его туда ввели разработчики языка — думаю первые, кто знает, что делают), но с умом.
упрощенная схема рассуждений такая:
в большинстве случаев goto не нужен,
остальные — спорные случаи: в них без goto сильно хуже не станет, дублирование кода всегда можно убрать
поэтому выбор: взять правило «без goto» и закрыть спорные случаи, либо каждый раз думать, можно ли здесь goto — самое главное, не при написании, а при прочтении чужого; как следствие, тратить время на споры и обсуждения, часто бесполезные — когда очень умный падаван защищает свое право на нестандартный код. Особенно когда под «спорные» случаи подтягиваются заведомые косяки.
Я выбираю взять жесткий гайдлайн, чтобы меньше думать и тем более меньше спорить :) Потому что человеческий фактор очень влияет на разработку, но это мало кто понимает, в спорной ситуации сосредотачиваясь на текущей конкретной проблеме, а не на том, что каждое решение влияет на весь наш путь:
«посеешь поступок — пожнешь привычку; посеешь привычку — пожнешь характер; посеешь характер — пожнешь судьбу»
в большинстве случаев goto не нужен,
остальные — спорные случаи: в них без goto сильно хуже не станет, дублирование кода всегда можно убрать
поэтому выбор: взять правило «без goto» и закрыть спорные случаи, либо каждый раз думать, можно ли здесь goto — самое главное, не при написании, а при прочтении чужого; как следствие, тратить время на споры и обсуждения, часто бесполезные — когда очень умный падаван защищает свое право на нестандартный код. Особенно когда под «спорные» случаи подтягиваются заведомые косяки.
Я выбираю взять жесткий гайдлайн, чтобы меньше думать и тем более меньше спорить :) Потому что человеческий фактор очень влияет на разработку, но это мало кто понимает, в спорной ситуации сосредотачиваясь на текущей конкретной проблеме, а не на том, что каждое решение влияет на весь наш путь:
«посеешь поступок — пожнешь привычку; посеешь привычку — пожнешь характер; посеешь характер — пожнешь судьбу»
Вы непоколебимы :)
В таких случаях — да, и это бывает минусом:
когда распинаешься перед программистом, объясняешь, почему и как, каких проблем это позволяет избежать, а потом он все равно делает так, как ты когда-то делал, но давно перестал, — потому что он и проблемы эти считает смехотворными, и что он на такие грабли не наступит.
И только постепенно, потратив кучу общего времени и нервов, понимаешь, что твое чутье — это чутье «заднего места», что нужно сначала сесть на кактус, чтобы потом его замечать заранее. Но всегда остается вопрос — где позволить ошибиться, а где постараться убедить сразу, так как ты за все отвечаешь, и как обойтись без авторитарности. Но потом и этому учишься )
когда распинаешься перед программистом, объясняешь, почему и как, каких проблем это позволяет избежать, а потом он все равно делает так, как ты когда-то делал, но давно перестал, — потому что он и проблемы эти считает смехотворными, и что он на такие грабли не наступит.
И только постепенно, потратив кучу общего времени и нервов, понимаешь, что твое чутье — это чутье «заднего места», что нужно сначала сесть на кактус, чтобы потом его замечать заранее. Но всегда остается вопрос — где позволить ошибиться, а где постараться убедить сразу, так как ты за все отвечаешь, и как обойтись без авторитарности. Но потом и этому учишься )
Неделя GOTO на хабре? :)
Жутчайшее форматирование кода.
А ещё я всё ещё помню, что в Google C++ Coding Standards исключениями пользоваться не разрешают из-за того, что с самого начала писали без исключений, а теперь есть шанс нарваться на нетривиальные проблемы, когда код, не использующий исключения, начнёт вызывать код с исключениями. Вот так. Взяли и запретили ;-)
Отличная статья, спасибо.
Sign up to leave a comment.
GOTO в прикладном программировании