18

一般に、GOTO を避けることをお勧めします。それを念頭に置いて、私はこのトピックについて同僚と議論してきました.

次のコードを検討してください。

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

ループ ラベルの使用は goto としてカウントされますか?

これは perldoc の perlsynが言わなければならないことです:

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 であるかどうかを説明する説得力のある議論またはドキュメントを探しています。また、なぜこれが perl で良い習慣と見なされるか、または考慮されないかについての説明も受け入れます。

4

11 に答える 11

29

ダイクストラの意図は、gotoに似たものが有害であると見なされることではありませんでした。ほぼすべての種類のプログラムフロー変更の主要な構成要素としてgotosが使用されるコードの構造は、今日私たちがスパゲッティコードと呼ぶものになるということでした。

元の記事を読んで、ラベル付きジャンプがほぼすべてのプログラミング言語の主要なフロー制御構造であった1968年に書かれたことを覚えておいてください。

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

于 2010-06-21T14:07:02.927 に答える
16

GOTO ラベルの危険性は、スパゲッティ コードを作成し、ロジックを判読不能にすることです。この場合、どちらも発生しません。GOTO ステートメントを使用することには多くの妥当性があり、その多くは Donald Knuth [記事] から得られます。

C と Perl の例の違いを掘り下げて... C プログラムのアセンブリ レベルで何が起こっているかを考えると、とにかくすべて GOTO にコンパイルされます。また、MIPS やその他のアセンブリ プログラミングを行ったことがある場合は、それらの言語のほとんどにループ構造がなく、条件分岐と無条件分岐だけがあることがわかります。

最終的には、読みやすさとわかりやすさに帰着します。どちらも、一貫性があることで非常に大きな助けになります。あなたの会社にスタイル ガイドがある場合は、それに従います。それ以外の場合は、perl スタイル ガイドに従うのが良い考えのように思えます。そうすれば、将来、他の perl 開発者があなたのチームに加わったときに、彼らはすぐに作業を開始し、あなたのコード ベースに慣れることができます。

于 2010-06-21T14:12:29.783 に答える
5

コードを理解しやすくする限り、gotoとしてカウントされるかどうかは誰が気にしますか?多くの場合、gotoを使用すると、if()およびループ条件で多数の追加テストを実行するよりも読みやすくなります。

于 2010-06-21T14:07:13.247 に答える
3

IMO、あなたのコード比較は不公平です。目標は読みやすいコードです。

公平を期すために、慣用的な Perl のネストされたループとラベルのないループを比較する必要があります。C スタイルの for およびブロックされた 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 > $jetlastラベルがない場合は、内側のループで動作し、内側のループが完了すると外側のループの次の項目に移動することを覚えておく必要があります。これは正確にはロケット科学ではありませんが、実際の実証可能な認知オーバーヘッドです。ラベルを正しく使用すると、コードが読みやすくなります。

アップデート:

ネストされたループの後にコードがあるとどうなるか、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.
       }
于 2010-06-21T18:40:51.537 に答える
2

区別はやや曖昧だと思いますが、goto perldocが(眉をひそめている)gotoステートメントについて次のように述べています。

goto-LABELフォームは、LABELというラベルの付いたステートメントを見つけ、そこで実行を再開します。

..。

Perlの作者は、この形式のgotoを使用する必要性を感じたことはありません(Perlでは、Cは別の問題です)。(違いは、Cはループ制御と組み合わせた名前付きループを提供しないことです。Perlは提供します。これは他の言語でのgotoのほとんどの構造化された使用に取って代わります。)

ただし、 perlsynperldocは次のように述べています。

whileステートメントは、式がtrueである限りブロックを実行します。式がfalseである限り、untilステートメントはブロックを実行します。LABELはオプションであり、存在する場合は、識別子とそれに続くコロンで構成されます。LABELは、ループ制御ステートメントnext、last、およびredoのループを識別します。LABELが省略されている場合、ループ制御ステートメントは最も内側の囲んでいるループを参照します。これには、実行時にコールスタックを動的に振り返ってLABELを見つけることが含まれる場合があります。このような絶望的な動作は、use warningsプラグマまたは-wフラグを使用すると、警告をトリガーします。

絶望的な振る舞いのビットは私にはあまり良く見えませんが、私はその意味を誤解している可能性があります。

Learning Perlの本(第5版、162ページ)には、次のように書かれています。

最も内側のループブロックではないループブロックを操作する必要がある場合は、ラベルを使用します。

..。

ラベルがブロック全体に名前を付けていることに注意してください。コード内のターゲットポイントをマークしていません。[結局、これは後藤ではありません。]

それは物事を片付けるのに役立ちますか?おそらくそうではありません...:-)

于 2010-06-21T14:47:01.937 に答える
2

breakPerlのラベル付きループジャンプは、Cと同じくらいGOTOcontinueです。

于 2010-06-21T14:47:51.110 に答える
2

私は次のように答えますが、これが他の人が言ったことと十分に異なるかどうかはわかりません。

現在のスコープ内または親スコープにのみ移動できるため、通常は で暗示されるものよりもはるかに危険性が低くなり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 で s のないループに遭遇すると、ループを書き直します。

于 2010-06-21T16:51:17.227 に答える
1

この種のジャンプは、gotoのようなステートメントの統制のとれた使用法です。したがって、gotoを無秩序に使用するよりも確かに害は少ないです。(kasperjjが書いたように、「ダイクストラ法の意図は、gotoに似たものが有害であると見なされることではありませんでした。」

IMO、このPerlの種類のジャンプは、Cの「ブレーク」および「続行」よりも優れた設計です。これは、ブレークまたは続行するループが明確になり、コードの変更に直面してもより確実になるためです。(さらに、外側のループを中断または継続することもできます。)

ブレイク/コンティニューなどが嫌いな専門家もいますが、ある時点で経験則と読みやすさの間にトレードオフがあり、適切に選択されたブレイク/コンティニューまたは後藤でさえ「政治的に」よりも読みやすくなる可能性があります正しい」コード。

于 2010-06-21T16:29:21.610 に答える
1

ブレーク/ラストとコンティニュー/ネクストはgotoです。なぜ誰かがキーワードを避けるためにそのような長さに行くのに同じことをする別のキーワードを使うのか理解できません...

于 2010-06-21T16:30:02.760 に答える
1

gotos理解しにくいコード、特に「スパゲッティコード」と呼ばれるものを作成するため、悪い結果になります。何がわかりにくいですnext Line...か?

これをループの「名前」と呼ぶことができます。これは、ループの境界を強調するのに役立ちます。ループに関連して任意のポイントにジャンプしているわけではありません。ループの先頭に戻ります。

悲しいことに、それがグループまたは家の標準である場合、それが後藤ではないことをグループに納得させるものは何もないかもしれません。私には、三項演算子が物事を読みにくくすることを絶対に主張するマネージャーがいて、すべてにifブロックを使用することを好みました。if-elseの句で何でもできるというかなり良い議論がありましたが、3項によって、特定の値を探していることが明示されました。売り上げ無し。

于 2010-06-21T15:39:53.600 に答える
1

4.4.4. ループ制御

LABEL をループに配置して名前を付けることができると述べました。ループの LABEL は、ループ制御演算子 next、last、および redo のループを識別します。LABEL は、ループの先頭だけでなく、ループ全体に名前を付けます。したがって、ループを参照するループ制御演算子は、実際にはループ ラベル自体に「移動」しません。コンピューターに関する限り、ラベルはループの最後に配置することも簡単にできます。しかし、何らかの理由で、人々は上部にラベルが付けられたものを好みます。

Perl のプログラミング

于 2010-12-23T14:09:18.990 に答える