Метки цикла Perl считаются GOTO?

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

рассмотрим следующий код:

Line:
    while( <> ) {
        next Line if (insert logic);
    }

считается ли использование метки цикла как goto?

вот что perlsyn в perldoc должен сказать:

вот как программист C может закодировать определенный алгоритм в Perl:

for (my $i = 0; $i < @ary1; $i++) {
    for (my $j = 0; $j < @ary2; $j++) {
        if ($ary1[$i] > $ary2[$j]) {
            last; # can't go to outer :-(
        }
        $ary1[$i] += $ary2[$j];
    }
    # this is where that last takes me
}

тогда как вот как программист Perl, более удобный с идиомой, может это сделать:

OUTER: for my $wid (@ary1) {
    INNER:   for my $jet (@ary2) {
                 next OUTER if $wid > $jet;
                 $wid += $jet;
             }
       }

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

11 ответов


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

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

http://userweb.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF


опасность ярлыков GOTO заключается в том, что они создают код спагетти и делают логику нечитаемой. Ни то, ни другое в данном случае не произойдет. Существует много обоснованности в использовании заявлений GOTO, большая часть защиты исходит от Дональда Кнута [статьи].

углубляясь в различия между вашим примером C и Perl... Если вы рассмотрите, что происходит на уровне сборки с вашими программами C, все это в любом случае компилируется до GOTOs. И если вы сделали какие-либо MIPS или другое Программирование сборок, тогда вы видели, что большинство этих языков не имеют никаких циклических конструкций, только условные и безусловные ветви.

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


кого волнует, считается ли это goto, пока это облегчает понимание кода? Использование goto часто может быть более читаемым, чем куча дополнительных тестов в условиях if() и loop.


IMO, ваше сравнение кода несправедливо. Цель читаемый код.

чтобы быть справедливым, вы должны сравнить идиоматический вложенный цикл Perl с метками против одного без них. Оператор C style for и blocked if добавляет шум, который делает невозможным сравнение подходов.

метки:

OUTER: for my $wid (@ary1) {
    INNER:   for my $jet (@ary2) {
                 next OUTER if $wid > $jet;
                 $wid += $jet;
             }
       }

без этикеток:

for my $wid (@ary1) {
   for my $jet (@ary2) {
       last if $wid > $jet;
       $wid += $jet;
   }
}

Я предпочитаю помеченную версию, потому что она явно указывает на эффект условия $wid > $jet. Без этикетки вы должны помнить, что last работает на внутреннем цикле, и когда внутренний цикл сделан, мы переходим к следующему элементу во внешнем цикле. Это не совсем ракетостроение, но это реальные, доказуемые, когнитивные накладные расходы. При правильном использовании метки делают код более читаемым.

обновление:

stocherilac спрашивает, Что произойдет, если у вас есть код, после вложенного цикла. Это зависит от того, хотите ли вы пропустить его на основе внутреннее условное или всегда выполняйте его.

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

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

OUTER: for my $wid (@ary1) {
    INNER:   for my $jet (@ary2) {
                 next OUTER if $wid > $jet;
                 $wid += $jet;
             }
       }
       continue {
           # This code will execute when next OUTER is called.
       }

Я думаю, что различие несколько нечеткое, но вот что goto perldoc заявляет о (нахмурился) goto заявление:

форма goto-LABEL находит оператор, помеченный меткой, и возобновляет выполнение там.

...

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

на perldoc perlsyn, однако, говорит это:

оператор while выполняет блок до тех пор, пока выражение истинно. Оператор until выполняет блок до тех пор, пока выражение имеет значение false. Метка является необязательной и, если она присутствует, состоит из идентификатора, за которым следует двоеточие. Метка определяет цикл для операторов управления циклом следующий, последний и повтор. Если метка опущена, оператор управления циклом ссылается на самый внутренний охватывающий цикл. Это может включать динамический просмотр стека вызовов во время выполнения, чтобы найти метку. Такое отчаянное поведение вызывает предупреждение, если вы используете прагму use warnings или флаг-w.

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

на Изучение Perl книга (5-й издание, стр. 162) сказано:

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

...

обратите внимание, что метка называет весь блок; это не маркировка целевой точки в коде. - "Это не перейти в конце концов.]

Это помогает прояснить ситуацию? Скорее всего, нет... :-)


помеченные циклические прыжки в Perl являются GOTOs столько, сколько c break и continue есть.


я бы ответил так, и я не уверен, что это достаточно отличается от того, что говорили другие:

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

if (1) {
  goto BAR;
  die 'bar'
}
BAR:

это должно работать, очевидно, но это не будет (не может двигаться в этом направлении).

if (0) {
  BAR:
  die 'bar'
}
goto BAR;

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

LOOP: while (1) {
  next LOOP if /foo;
}

как-то хуже, чем

while (1) {
  next if /foo/;
}

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

давайте посмотрим на другой пример

while (1) {

  while (1) {
    last;
  }

  stuff;

}

- vs -

FOO: while (1) {

  BAR: while (1) {
    next FOO;
  }

  stuff;

}

в последнем примере здесь next FOO переход stuff -- вы можете пожелать этого, но это плохая идея. Это означает, что программист прочитал родительскую область до завершения, чего, вероятно, лучше избегать. В резюме, label не так плохо, как goto и иногда они могут упростить код, но в большинстве случаев их следует избегать. Я обычно перепишите циклы без labels, когда я встречаю их на CPAN.


gotos плохи, потому что они создают трудный для понимания код-в частности, то, что часто называют "спагетти-кодом". Что трудно понять, о next Line...??

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

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


этот вид прыжка является дисциплинированным использованием Гото-подобного утверждения. Так что это, безусловно, менее вредно, чем недисциплинированное использование goto. (Как писал каспердж:"dijkstras намерение никогда не было, что что-то похожее на goto следует считать вредным.")

IMO, этот вид прыжка Perl даже лучше, чем " перерыв "и" продолжить "C, потому что ясно, какой цикл мы нарушаем или продолжаем, и это делает его более твердым перед лицом изменений кода. (Кроме того, это также позволяет прервать или продолжить внешний цикл.)

есть эксперты, которые не любят перерыв/продолжить и тому подобное, но в какой-то момент есть компромисс между эмпирическими правилами и удобочитаемостью, и хорошо выбранный перерыв/продолжить или даже Гото может стать более читаемым, чем "политически правильный" код.


break / last и continue / next являются gotos. Я не понимаю, почему кто-то пошел бы на такие меры, чтобы избежать ключевого слова, но использовать другое ключевое слово, которое делает то же самое...


4.4.4. Управление Петлей

мы упомянули, что вы можете поставить метку на цикл, чтобы дать ему имя. Метка цикла определяет цикл для операторов управления циклом next, last и redo. Метка называет цикл в целом, а не только верхнюю часть цикла. Следовательно, оператор управления циклом, ссылающийся на цикл, фактически не" переходит " к самой метке цикла. Что касается компьютера, то метка с таким же успехом могла быть помещена в конец цикла. Но людям нравится вещи, помеченные наверху, по какой-то причине.

Программирование На Perl