Действительно ли 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;
             }
       }

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

18
задан Ether 21 June 2010 в 15:13
поделиться

10 ответов

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

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

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

29
ответ дан 30 November 2019 в 05:57
поделиться

Помеченные переходы цикла в Perl являются GOTO в той же степени, что и C break и continue .

2
ответ дан 30 November 2019 в 05:57
поделиться

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

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

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

1
ответ дан 30 November 2019 в 05:57
поделиться

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

5
ответ дан 30 November 2019 в 05:57
поделиться

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

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

...

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

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

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

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

В книге Learning Perl (5-е издание, стр. 162) сказано следующее:

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

...

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

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

2
ответ дан 30 November 2019 в 05:57
поделиться

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

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

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

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

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

Многие случаи использования меток отличаются от 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.

2
ответ дан 30 November 2019 в 05:57
поделиться

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

Честно говоря, вам следует сравнить идиоматический вложенный цикл Perl с метками с циклом без них. Стиль C для оператора if и заблокированный оператор 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.
       }
3
ответ дан 30 November 2019 в 05:57
поделиться

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

1
ответ дан 30 November 2019 в 05:57
поделиться

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

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

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

16
ответ дан 30 November 2019 в 05:57
поделиться

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

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

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

1
ответ дан 30 November 2019 в 05:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: