私は別のフォーラムにコードスニペットを投稿して助けを求めましたが、GoTo
ステートメントを使用することは非常に悪いプログラミング慣行であると人々が指摘しました。私は疑問に思っています:なぜそれは悪いのですか?
VB.NETで使用するGoTo
の代替手段はありますか?一般的にはより良い方法と見なされますか?
ユーザーが生年月日を入力する必要がある以下のスニペットについて考えてみます。月/日付/年が無効または非現実的である場合は、ループバックしてユーザーにもう一度質問したいと思います。 (私は整数のサイズをチェックするためにifステートメントを使用しています...これを行うためのより良い方法がある場合は、それも教えていただければ幸いです:D)
retryday:
Console.WriteLine("Please enter the day you were born : ")
day = Console.ReadLine
If day > 31 Or day < 1 Then
Console.WriteLine("Please enter a valid day")
GoTo retryday
End If
私は他の人とは異なり、GOTO自体がすべての悪ではないということを言います。悪はGOTOの誤用から来ています。
一般に、GOTOを使用するよりも優れたソリューションがほとんどの場合ありますが、GOTOがそれを行う適切な方法である場合もあります。
そうは言っても、あなたは初心者なので、GOTOが適切かどうかを判断することは、もう数年は許されるべきではありません(ほとんどないため)。
私はあなたのコードをこのように書くでしょう(私のVBは少しさびています...):
Dim valid As Boolean = False
While Not valid
Console.WriteLine("Please enter the day you were born: ")
Dim day As String
day = Console.ReadLine
If day > 31 Or day < 1 Then
Console.WriteLine("Please enter a valid day.")
Else
valid = True
End If
End While
GOTOコードを取得してそれを見ると、誰かが最初にどのようにコードにアプローチしますか? 「うーん..retryday?これは何をしますか?これはいつ起こりますか?ああ、その日が範囲外の場合はそのラベルに移動します。わかりました。日付が有効で範囲内にあると見なされるまでループします。」 。
一方、私のものを見ると:
「ああ、有効になるまでこれを続けたい。日付が範囲内にあるときに有効だ」
http://xkcd.com/292/ これは後藤の標準的な意見だと思います。
代わりに、dowhileループを試して使用してください。 do whileループは常に一度実行され、ユーザーを保護する必要がある場合に最適ですが、正しい情報を入力していることを確認してください。
Sub Main()
Dim valid as Integer
valid=0
Do Until valid = 1
System.Console.WriteLine("enter the day ")
day = System.Console.ReadLine()
If day > 31 Or day < 1 Then
System.Console.WriteLine("Invalid day\n")
valid = 0;
Else
valid = 1
Loop
End Sub
GOTO
コンストラクトは、スパゲッティコードを生成します。これにより、コードをトレースすることはほとんど不可能になります。
手続き型/関数型プログラミングは、はるかに優れたアプローチです。
GoTo
ステートメントのメリット(またはむしろその欠如)に関する質問は、このサイトで根強いものです。例については、ここをクリックしてください: GoToはまだ有害であると見なされていますか?
GoTo
の代替案に関しては、提供されているスニペットでは、while
ループがうまく機能します。
day = -1
While (day < 0)
Console.WriteLine("Please enter the day you were born : ")
day = Console.ReadLine
If day > 31 Or day < 1 Then
Console.WriteLine("Please enter a valid day")
day = -1
End If
End While
GOTOはかなり政治的な問題です。 GOTOの「解決策」は、関数、メソッド、ループなどの他の組み込みナビゲーション構造を使用することです。VBの場合、そのコードを実行するサブプロシージャを作成するか、Whileループに入れることができます。あなたはそれらの主題の両方をかなり簡単にグーグルで検索することができます。
少し不格好ですが:
Dim bContinue As Boolean
Console.WriteLine("Enter a number between 1 and 31")
Do
Dim number As Integer = Console.ReadLine()
If number >= 1 AndAlso number <= 31 Then
bContinue = True
Else
Console.WriteLine("Please enter a VALID number between 1 and 31")
End If
Loop Until bContinue
「gotoland」のいくつかの基本的なループも考慮してください
Dim i As Integer
startofloop1:
Debug.WriteLine(i)
i += 1
If i <= 10 Then
GoTo startofloop1
End If
i = 0
startofloop2:
Debug.WriteLine(i * 2)
i += 1
If i <= 10 Then
GoTo startofloop2
End If
これがNiceの同等物です:
For x As Integer = 0 To 10
Debug.WriteLine(i)
Next
For x As Integer = 0 To 10
Debug.WriteLine(i * 2)
Next
どちらが読みやすく、エラーが発生しにくいですか?
Gotoの使用は、何十年もの間悪い習慣と見なされてきました。おそらく、それは元のBASIC(Visual Basicより前)に対する反発でした。元のBASICには、whileループ、ローカル変数(グローバルのみ)、および(ほとんどのBASICバージョンでは)関数がパラメーターを受け取ったり値を返したりすることができませんでした。さらに、機能は明示的に分離されていませんでした。 RETURNステートメントを忘れた場合、制御は暗黙的に1つの関数から別の関数に落ちる可能性があります。最後に、コードのインデントは、これらの初期のBASICでは異質な概念でした。
元のBASICをしばらく使用した場合(私が行ったように)、グローバル変数とgotoをどこでも使用すると、大規模なプログラムが理解しにくくなり、細心の注意を払わずに、複雑な混乱に変わったことに気付くでしょう。 "スパゲッティ"。 WHILE..WENDループとSUBを備えたQBASICを学んだとき、私は決して振り返りませんでした。
ゴトが少しでも傷つくとは思いませんが、コーダー文化では、ゴトはどういうわけか邪悪であるという強い感覚が残っています。したがって、私は不快な感性を避ける以外の理由でゴトスを避けます。ときどき、gotoが問題をきれいに解決することがあります(内側のループ内から外側のループを抜け出すなど)が、別の解決策でコードが読みやすくなるかどうかを検討する必要があります(たとえば、外側のループを別の関数に入れて「内側のループで、gotoの代わりに「exitfunction」)。
私はおそらく100,000行のコードでC++プログラムを作成し、gotoを30回使用しました。一方、1,000を超える「通常の」ループと約10,000の「if」ステートメントがあります。
機能FTW!
さて、ここであなたのコードが本当にVB.Netであるかどうかはわかりません。なぜなら、いくつかの奇妙なタイプのものが進行しているからです(つまり、Console.Readline
はString
を返しますが、比較できる数値ではありません)...なので、今のところタイプについては忘れてしまいます。
Console.Writeline("Please enter the day you were born : ")
day = Console.Readline()
While not ValidDate(day)
Console.WriteLine("Please enter a valid day")
day = Console.Readline()
End While
そして別に
Function ValidDate(day) As Boolean
Return day > 31 Or day < 1
End Function
または、再帰とアーリーリターン構文を楽しむこともできます。 ;)
Function GetDate() As String
Console.Writeline("Please enter the day you were born : ")
day = Console.Readline()
If ValidDate(day) Then Return day 'Early return
Console.Writeline("Invalid date... try again")
GetDate()
End Function
While True
Console.WriteLine("Please enter the day you were born : ")
day = Console.ReadLine
If day > 31 Or day < 1 Then
Console.WriteLine("Please enter a valid day")
Continue
Else
Break
End If
End While
決定構造やループなどの単純な組み込み言語構造を使用して、GOTO
sで実行できるほとんどすべてのことを実行できます。また、GOTO
ステートメントは、多くの場合、厄介で理解できないスパゲッティコードになります。ループやifなどには、明確で、受け入れられ、理解できる使用法があります。
通常提案されているように、ダイクストラの 有害と見なされるGo-Toステートメント を参照してください。
私はここにいる他のみんなに同意しなければなりません:GOTO自体は悪ではありませんが、それを誤用すると確かにあなたの人生は悲惨になります。選択できる制御構造は他にもたくさんあり、適切に作成されたプログラムは通常、gotoなしでほとんどすべての状況を処理できます。そうは言っても、私は約15,000行を積み上げるプログラムの完了間近の段階にあり、GOTOステートメントを1つだけ使用しました(これは後で置き換える可能性があります)。私が扱った過去12ほどのプログラムでGOTOを使用したのはこれが初めてです。しかし、この例では、コンパイラエラーを取り除きました(同じSub内で異なるIf構造内でMe.Close()を2回使用しました。抑制できたはずですが、ラベルを挿入して1つのMe.Close()を次のように置き換えました。 GoTo CloseLabel)。このSub内でMe.Close()を必要とするインスタンスがさらに発生し始めた場合、Me.Close()を独自のサブに配置し、If構造または他のループからそのサブを呼び出すだけで終了する可能性があります。プログラムの...私が言ったように、代替手段がありますが、時々、そして非常にまれに、控えめに、そして戦略的に使用されるとき、GoToはまだ役に立ちます。スパゲッティコードに注意してください、それはまばたきの混乱です笑
有害と見なされるGo-Toステートメントのダイクストラのアドバイスに従うことがしばしば推奨されます。
ドナルド・クヌースはダイクストラにかなりしっかりと答えました。この例は、彼の反例の1つの最新バージョンです。個人的には、これに遭遇したときに内部ブレークを含む無限ループを作成しますが、GOTOステートメントを作成するまれなケースが他にもいくつかあります。
私にとって最も一般的なものは、深くネストされたループとこのパターンから抜け出すことです。
ContinueTry:
Try
'Worker code
Catch ex as IO.IOException
If MessageBox.Show(...) = DialogResult.Retry Then Goto ContinueTry
Throw
End Try
また、遷移を提供するgotoステートメントを持つ大規模な有限状態マシンの2つのケースがあります。
「本による」オオカミが反対票を投じても、私は私のものを投げます。見てください: ループと関数をサポートする言語で「goto」を使用することはこれまでに有利ですか?もしそうなら、なぜですか?