Подтвердить что ты не робот

Являются ли метки цикла Perl как GOTO?

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

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

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;
             }
       }

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

4b9b3361

Ответ 1

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

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

https://www.cs.utexas.edu/users/EWD/ewd02xx/EWD215.PDF

Ответ 2

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

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

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

Ответ 3

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

Ответ 4

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

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

Labels:

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 работает во внутреннем цикле и что, когда внутренний цикл завершен, мы переходим к следующему элементу во внешнем цикле. Это не совсем ракетостроение, но это реальные, очевидные, когнитивные накладные расходы. Правильно используемые метки делают код более удобочитаемым.

Update:

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.
       }

Ответ 5

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

Форма goto-LABEL находит выражение с меткой LABEL и возобновляет выполнение там.

...

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

perlsyn perldoc, однако, говорит следующее:

Оператор while выполняет блок, пока выражение истинно. Оператор until выполняет блок, пока выражение ложно. LABEL является необязательным, и если присутствует, состоит из идентификатора, за которым следует двоеточие. LABEL идентифицирует цикл для операторов управления циклом next, last и redo. Если LABEL опущена, оператор управления контуром относится к самому внутреннему замкнутому циклу. Это может включать динамический поиск вашего стека вызовов во время выполнения, чтобы найти LABEL. Такое отчаянное поведение вызывает предупреждение, если вы используете прагму предупреждений использования или флаг -w.

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

Книга обучения Perl (5-е издание, стр. 162) гласит:

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

...

Обратите внимание, что метка называет весь блок; это не указывает на целевую точку в коде. [Это не goto.]

Помогает ли это прояснить ситуацию? Вероятно, не...: -)

Ответ 6

Перемещенные петлевые переходы в Perl - это GOTO, такие как C break и continue.

Ответ 7

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

Поскольку вы можете перемещаться только внутри текущей области или в родительскую область, они гораздо менее опасны, чем то, что обычно подразумевается под 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 (что вы перезапускаете цикл в текущей области), и это лучше.

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

while (1) {

  while (1) {
    last;
  }

  stuff;

}

-vs -

FOO: while (1) {

  BAR: while (1) {
    next FOO;
  }

  stuff;

}

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

Ответ 8

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

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

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

Ответ 9

Этот вид прыжка является дисциплинированным, используемым в инструкции, подобной goto. Таким образом, это, безусловно, менее вредно, чем недисциплинированное использование goto. (Как писал kasperjj, "намерение Дейкстраса никогда не заключалось в том, что все, что похоже на goto, должно считаться вредным".)

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

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

Ответ 10

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

Ответ 11

4.4.4. Loop Control

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

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