今日、同僚に、自分が作成した2つのネストされたforループを通るフローを制御するためにlabelステートメントを使用するようにコードをリファクタリングするよう提案してもらいました。個人的にはプログラムの可読性が低下すると思うので、これまで使ったことがありません。しかし、議論が十分にしっかりしていれば、私はそれらを使用することについて私の考えを変えたいと思っています。ラベルステートメントに対する人々の意見は何ですか?
12 に答える
2 つのループ (または switch ステートメントを含むループ) を飛び越えることができれば、多くのアルゴリズムをより簡単に表現できます。気にしないでください。一方で、過度に複雑なソリューションを示している可能性もあります。ですから、立ち止まって問題を見てください。
すべてのループに対して「単一の入口、単一の出口」アプローチを好む人もいます。つまり、ループの中断 (および継続) と早期復帰を完全に回避します。これにより、一部のコードが重複する可能性があります。
私が絶対に避けたいのは、補助変数の導入です。状態内に制御フローを隠すと、混乱が生じます。
ラベル付きループを 2 つのメソッドに分割することは、難しい場合があります。例外はおそらく重すぎます。単一のエントリ、単一の出口アプローチを試してください。
ラベルは goto のようなものです: 控えめに使用し、コードをより速く、より重要なことに、より理解しやすくする場合にのみ使用してください。
たとえば、6 レベルの深さの大きなループにいて、残りのループを完了するのが無意味になる条件に遭遇した場合、ループを早く終了するために条件ステートメントに 6 つの余分なトラップ ドアを用意しても意味がありません。
ラベル (および goto のラベル) は悪いものではありません。単に、人々がラベルを悪い意味で使用することがあります。ほとんどの場合、実際にコードを書こうとしているのは、あなたや次のプログラマーが理解できるようにするためです。超高速にすることは二次的な問題です (時期尚早の最適化には注意してください)。
ラベル (および goto) が誤用されると、コードが読みにくくなり、あなたと次の開発者に悲しみをもたらします。コンパイラは気にしません。
ラベルが必要になる機会はほとんどなく、めったに使用されないため、混乱を招く可能性があります。ただし、1 つを使用する必要がある場合は、1 つを使用してください。
ところで:これはコンパイルして実行します。
class MyFirstJavaProg {
public static void main(String args[]) {
http://www.javacoffeebreak.com/java101/java101.html
System.out.println("Hello World!");
}
}
ラベルに代わるものが何であるか知りたいです。これは、「できるだけ早く戻る」と「変数を使用して戻り値を保持し、最後にのみ返す」という議論にほぼ要約されると思います。
ネストされたループがある場合、ラベルはかなり標準的です。それらが実際に読みやすさを低下させる唯一の方法は、別の開発者がそれらをこれまで見たことがなく、それらが何を意味するのか理解していない場合です。
Sieve メソッドの実装に Java ラベル付きループを使用して素数を見つけました (プロジェクトのオイラー数学の問題の 1 つに対して行われました)。これにより、ネストされたループと比較して 10 倍速くなりました。たとえば、if(特定の条件) は外側のループに戻ります。
private static void testByFactoring() {
primes: for (int ctr = 0; ctr < m_toFactor.length; ctr++) {
int toTest = m_toFactor[ctr];
for (int ctr2 = 0; ctr2 < m_divisors.length; ctr2++) {
// max (int) Math.sqrt(m_numberToTest) + 1 iterations
if (toTest != m_divisors[ctr2]
&& toTest % m_divisors[ctr2] == 0) {
continue primes;
}
} // end of the divisor loop
} // end of primes loop
} // method
私は C++ プログラマーに、ラベル付きループがいかに悪いかを尋ねました。たとえば、ネストされたループが 3 つあり、特定の条件で最も外側のループに戻りたい場合などです。
したがって、それらには用途があり、解決しようとしていた問題によって異なります。
新しい for-each ループを使用すると、ラベルが非常に明確になると思います。
例えば:
sentence: for(Sentence sentence: paragraph) {
for(String word: sentence) {
// do something
if(isDone()) {
continue sentence;
}
}
}
新しい for-each でラベルを変数と同じにすることで、それは本当に明確に見えると思います。実際、Java は悪であり、変数ごとに暗黙のラベルを追加する必要があるかもしれません。
一部の場所ではそれらを支持すると主張しますが、この例では特に有用であることがわかりました。
nextItem: for(CartItem item : user.getCart()) {
nextCondition : for(PurchaseCondition cond : item.getConditions()) {
if(!cond.check())
continue nextItem;
else
continue nextCondition;
}
purchasedItems.add(item);
}
Java コードで「実際に」使用されているラベルを見たことがありません。入れ子になったループを本当に壊したい場合は、メソッドをリファクタリングして、初期の return ステートメントが目的を達成できるかどうかを確認してください。
技術的には、アーリー リターンとレーベルの違いはあまりないと思います。ただし、実際には、ほぼすべての Java 開発者が早期復帰を経験しており、それが何をするかを知っています。多くの開発者は、少なくともラベルに驚き、おそらく混乱すると思います。
私は学校で単一のエントリ/単一の出口の正統性を教えられましたが、それ以来、コードを単純化して明確にする方法として、早期の return ステートメントとループの抜け出しを高く評価するようになりました。
コードでラベルを使用することはありません。ガードを作成し、nullまたはその他の異常な値に初期化することを好みます。多くの場合、このガードは結果オブジェクトです。同僚がラベルを使用しているのを見たことがなく、リポジトリにも見つかりませんでした。それはあなたのコーディングスタイルに大きく依存します。私の意見では、ラベルを使用すると読みやすさが低下します。これは一般的な構造ではなく、通常 Java では使用されないためです。
はい、ラベルを使用する特定の理由がない限り、ラベルの使用を避ける必要があります (アルゴリズムの実装を簡素化する例が適切です)。そのような場合、誰かが後でやって来て、「コードを改善する」または「コードの臭いを取り除く」または他の潜在的なBSの言い訳。
私は、この種の質問を、いつ三項式 if を使用すべきか使用すべきでないかを決定することと同一視します。主な理由は、読みやすさを妨げる可能性があり、プログラマーが合理的な方法で名前を付けることに非常に注意しない限り、ラベルなどの規則を使用すると事態がさらに悪化する可能性があるためです。「nextCondition」と「nextItem」を使用する例で、ラベル名に「loop1」と「loop2」を使用したとします。
個人的には、ラベルは、Assembly や BASIC、その他の同様に制限された言語以外では、あまり意味をなさない機能の 1 つです。Java には、より多くの従来型/通常のループおよび制御構造があります。