LINUX.ORG.RU

«break to out» from for/switch

 , , , ,


0

1

добрый день.
разбираю код (обычный си-код, без крестов):

1. switch вложен в for
2. выброс реализован «хаком» условия for

все работает, но при «обычном» многостраничном операторе switch-case такой выход «неочевиден».

вопрос: можно-ли как ни-будь красиво/очевидно/локонично реализовать выход из цикла

for(i=1; i<RET; i++)
{
...
switch(i)
  {
  case 10:
  if() 
    {
    ...
    RET = 11; // break to out
    }
  break;
  ...
  case 20:
  if() 
    {
    ...
    RET = 21;
    }
  break;
  ...
  case 30:
  if() 
    {
    ...
    RET = 31;
    }
  break;
  }   // switch()
}     // for()

★★★

goto

for(...) {
  switch() {
    case ...:
      ...
      goto exit_for;
  }
}
exit_for:

можно еще c continue поизвращаться

anonymous ()

Оберни свой for(...){switch(...){...}} в отдельную функцию и делай return вместо RET = 21. Если переживаешь за оптимизацию, добавь #inline

Aswed ★★★★★ ()
Ответ на: комментарий от Aswed

в отдельную функцию

Не всегда подходит. А так да, этот метод способствует структурированию кода

anonymous ()

На что только люди не идут, лишь бы не использовать goto. Хотя как раз выход из вложенного цикла — абсолютно not harmful case его использования.

anonymous ()
#include <stdio.h>

int main(int argc, char *argv[])
{
    int ret = 11;

    for (int i = 0,stop = 0; i < ret && stop != 1; ++i)
    {
        switch(i)
        {
            case 10: if(...) stop=1; continue;
            default:break;
        };
        printf("%i\n",i);
    }
    return 0;
}
LINUX-ORG-RU ★★★★★ ()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 4)

ну ребята, спасибо... конечно :о) но решил более очевидным и подходящим способом... :о) переИменовал переМенную условия ... она до «этого» была с менее понятным именем

for(i=1; i<BREAK; i++)
...
    BREAK = xxx;

как говориться в классике

- это и охота и убивать ни кого не надо :о)

sunjob ★★★ ()
Последнее исправление: sunjob (всего исправлений: 1)
  1. switch вложен в for
  2. выброс реализован «хаком» условия for

Как правило, если этот код завернуть в функцию (или несколько функций) то лишние break исчезнут или станут return.

AlexVR ★★★★★ ()
Ответ на: комментарий от AlexVR

и ... если главная функция состоит только из этого самомго for() то тогда код повысит свою дебильность и неудобочитабельность...:о)

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

имхо

решение найдено! но если есть еще идеи, обязательно предлагайте!

спасибо

sunjob ★★★ ()
Ответ на: комментарий от sunjob

Упс, чушь сморозил. Блин лучше уж RET = 0 писать для завершения цикла. А так переделай в функцию и выходи по return.

AlexVR ★★★★★ ()
Ответ на: комментарий от Psilocybe

согласен! просто для «быстрого вкуривания» решил обойтись «просто переименованием»

спасибо

sunjob ★★★ ()
Ответ на: комментарий от sunjob

и … если главная функция состоит только из этого самомго for() то тогда код повысит свою дебильность и неудобочитабельность…:о)

bool work_complete(int i, ....) {
  switch ...
}

for(int i ...) {
  ...
  if (work_complete(i, ...)) break;
}
AlexVR ★★★★★ ()
Ответ на: комментарий от Psilocybe

break с меткой это goto и есть, только с запутанным видом

firkax ★★★ ()
Ответ на: комментарий от AlexVR

а если таких switch-case() в функции с десяток... :о)

... то тогда код повысит свою дебильность и неудобочитабельность

и/или ... наплодим сущностей, необходимых только внутри одной единственной функции

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

пусть уж лучше будет одна единственная «дебильная» функция

спасибо

sunjob ★★★ ()
Последнее исправление: sunjob (всего исправлений: 1)

Хак уродский, лучше так не делать

Есть такое ключевое слово continue. Комбинируя его с break, можно сделать то что ты хочешь

Crocodoom ★★★★★ ()
Ответ на: комментарий от Crocodoom

так блин жешь :о)

BREAK = 0;
or
CNT = BREAK;
и есть этот самый аналого continue а break и так по умолчанию уже есть...

или я не правильно понял?!

sunjob ★★★ ()

тут вообще for+switch ненужен..условия-же подряд перебираются в порядке i, одно за другим.

приведённая конструкция (в псевдокоде)

for(int i=1;i<RET....)
switch(i) {
  case 1: if (condition_1) { action1(); i=RET;} break;
  case 2: if (condition_2) { action2();i=RET;} break;
}

аналогично совсем-совсем простому:

if (codition_1) { action1(); }
else if(condition_2) { action2();}
...

MKuznetsov ★★★★★ ()

единственный верный совет с функцией. остальное - быдлокод. goto ни при каких случаях нельзя использовать

tz4678 ★★ ()
Ответ на: комментарий от tz4678

Аргументы? Типичный пример использования goto - обработка ошибок. В ядре Linux это используется повсеместно. По твоему мнению это быдлокод?

anonymous ()
Ответ на: комментарий от anonymous

а то вы сами не знаете... то-то наши ядра дают стране угля :о)

как в том анектоде:

Ой, я вас умоляю! Вы посмотрите на этот мир и на эти брюки!

sunjob ★★★ ()
Ответ на: комментарий от anonymous

его давно хотели выпилить, но из-за кода, написанного «оптимизаторами» не решились, тем более многих оптимизаторов, авторов тех строчек и в живых уже нет

tz4678 ★★ ()
Ответ на: комментарий от anonymous

таки кланяюсь во все стороны, сползаю с табуретки, одеваю тапочки

sunjob ★★★ ()

break to out

что мосье хотел этим сказать?

anonymous ()
Ответ на: комментарий от sunjob

не в данном случае :о)

в данном случае именно так..

более того - взять из реальной жизни иной случай довольно проблемно. комбинация for/switch как-бы намекает что всё через жопу и масса лишнего

MKuznetsov ★★★★★ ()
Ответ на: комментарий от tz4678

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

и что уж точно быдлокод - писать функцию, с надеждой, что компилятор ее заинлайнит.

вызов функции, это всего лишь джамп на адрес. с сохранением адреса возврата на стек. goto это тоже джамп на адрес.

таким образом goto ручками, это полный аналог вызова функции, из которой возращаться не надо, то есть как бы заинлайненой.

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

alysnix ★★ ()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от MKuznetsov

for/switch как-бы намекает
всё через жопу
масса лишнего

блеск! анекдот напомнило

- Садись, Торчун - это пять!

как вам будет угодно!

sunjob ★★★ ()
Ответ на: комментарий от alysnix

правильно...!!! пишите много, пишите качественно, проверяйте все сами, набивайте шишки! как говориться, на здоровье!

sunjob ★★★ ()
Ответ на: комментарий от alysnix

ага экономим спички… и циклы развертываем, а то вдруг коНпилятор не справится. ты мне лучше приведи пример программы, где без goto нельзя обойтись. в других языках есть тоже табуированные операторы, например, with в JavaScript, global php, python (чит: используем возможности ООП)…

tz4678 ★★ ()
Ответ на: комментарий от tz4678

учите человека с самих ногтей все неправильно делать. пусть он лучше тупо придерживается правила не использовать никогда и нигде goto, а почему он сам допетрит через пару лет

tz4678 ★★ ()
Ответ на: комментарий от tz4678

Вроде, Дейкстра доказал, что структурное программирование (где нет goto) равносильно бесструктурному программированию.

anonymous ()
Ответ на: комментарий от grem

а давайте переменные q,p,q,r,t называть, везде пихать goto и побольше макросов, чтобы все состояло из макросов, и каждый цикл чтобы был развернут

tz4678 ★★ ()
Ответ на: комментарий от tz4678

Никто не предлагает пихать goto везде.

То есть ты своим странным примером утверждаешь, что авторы darktable не умеют писать код на C?

grem ★★★★★ ()
for() {
  if ( i == 10 && ** ) { break }
  if ( i == 20 && ** ) { break }
}
menangen ★★★★★ ()
Ответ на: комментарий от alysnix

и что уж точно быдлокод - писать функцию, с надеждой, что компилятор ее заинлайнит.

С надеждой - да, быдлокод. Если писать функцию с уверенностью, что компилятор её заинлайнит, то всё будет хорошо, и именно так и надо делать.

Я не против goto. Это тоже инструмент, у которого есть своя область применения. Для разных задач есть свои инструменты. Для конкретно данного случая функция предпочтительней, чем goto, т.к. она ясно выделяет в коде обработку ситуации выхода из цикла, а с goto при большом теле switch для понимания логики работы в голову придётся загружать лишние сущности.

Sorcerer ★★★★★ ()
Ответ на: комментарий от Sorcerer

писать функцию с уверенностью, что компилятор её заинлайнит

В С++ вроде сильно зависит от компилятора. Я где-то вычитал, что стоит только смотреть дизасм, заинлайнилось ли по факту или нет. На https://isocpp.org/wiki/faq/inline-functions решил проверить, есть ли гарантии, но там конкретики особо нет.

Инфы нашёл больше про гарантии, когда точно не будет inline: рекурсии с порогом вложений, превышающий установленный, или явное использование опций а-ля -no-inline. Конкретно эти условия можно обойти с помощью __attribute__ ((always_inline)), но на этом дело заканчивается.

В С есть более строгие гарантии inline или речь про опыт работы с конкретным компилятором?

PhysShell ()
Ответ на: комментарий от tz4678

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

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

пример - надо слить, сравнить или еще что - два файла, возвратить bool - успех/неуспех.

нетривиальность выхода из блока тут в том, что если файлы открыты - их надо закрыть.

то есть, если вы хотите выйти из нескольких мест такого блока например с ошибкой, то либо вам нужно копипейстить код закрытия файлов, …либо никак.

тогда как самый короткий и оптимальный тут код - c goto типа (некий псевдокод)

{
  file a = open(...)
  file b = open(...)

  ...
  if(something_wrong0) goto FAILED__;
  ...
  if(something_wrong1) goto FAILED__;

...
  close(a); //here all is correct
  close(b);
  return true;

FAILED__: //exit if failed
  close(a); 
  close(b); 
  log("critical operation bla-bla failed!");
  return false;
}

raii не рассматриваем, пусть это код сишный.

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

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

плохой goto - который эмулирует стандартный структурный оператор или пересекает границы блока.

alysnix ★★ ()
Ответ на: комментарий от anonymous

там обфусцированный код. что ты им хотел показать/доказать? и в исходниках на гитхабе нету ниодного with…

tz4678 ★★ ()
Последнее исправление: tz4678 (всего исправлений: 1)
Ответ на: комментарий от alysnix
file a = open(...);
file b = open(...);
bool res = handle(a, b); 
close(a); 
close(b);
if (!res)
  log("critical operation bla-bla failed!");
return res;

//
bool handle(...) {
  // ...
  if(something_wrong0) return false;
  // ...
  return true;
}
tz4678 ★★ ()
Последнее исправление: tz4678 (всего исправлений: 1)
Ответ на: комментарий от tz4678

а в чем «преимущество»??

вы ясночитаемый код превратили в две функции(зачем тут лишняя функция?), причем количество параметров функции handle по сути ничем не ограничено, это зависит от исходной сложности задачи(может там 100 ресурсов возникает? и их надо передать в handle)..

вы положили на инкапсуляцию - зачем вытаскивать из исходной функции реализацию наружу?

вы не показали общее решение - а если таких блоков(которые надо завершить нетривиально) внутри функции много?

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

вам нужен пример функции, где таких блоков с нетривиальным завершением например 5-10, часть из них вложенные, и семантика там сложная(чтобы список параметров ваших handle медом не казался).

а на вопрос - ну и зачем писать всю запутанную эту мутоту? - ответ только один… а чтобы удовлетворить формальному, неизвестно откуда взятому правилу, что goto это плохо…

да выкиньте вы это правило в помойку. goto хорошо, когда оно помогает компактности и читаемости кода.

alysnix ★★ ()
Последнее исправление: alysnix (всего исправлений: 1)

Я тоже проголосую за goto. Никогда не голосовал, но в этот раз обязательно проголосую, — оператор от народа!

TimofejShumilkin ()
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.