差分

このページの2つのバージョン間の差分を表示します。

この比較画面へのリンク

両方とも前のリビジョン前のリビジョン
次のリビジョン
前のリビジョン
tutorial:odin:control_flows [2025/05/19 08:13] – [do ... while は存在しない] freemikantutorial:odin:control_flows [2025/05/20 02:03] (現在) – 削除 freemikan
行 1: 行 1:
-====== Odinの制御フロー ====== 
- 
- 
-Odinの制御フローに関する機能は、全て文 (statement)です。 
-最近の言語では式 (expression)として提供するようになっている傾向がありますが、Odinはそうではありません。 
-伝統的な立場をとっています。 
- 
- 
-===== if文 ===== 
- 
-条件によって処理を分岐するにはif文を使います。 
-最も基本的なパターンは次のような形式です。 
- 
-<file odin> 
-if 条件文 { 
- // 条件がtrueのとき実行される 
-} else { 
- // 条件がfalseのとき実行される 
-} 
-</file> 
- 
-elseブランチは必要なければ省略可能です。 
- 
-''else''のあとに''if''を書くことで、追加の条件ブランチを書くことができます。 
- 
-<file odin> 
-if 条件文1 { 
- // 条件1がtrueのとき実行される 
-} else if 条件文2 { 
- // 条件1がfalseで、条件2がtrueのとき実行される 
-} else { 
- // 条件1がfalseで、条件2もfalseのとき実行される 
-} 
-</file> 
- 
-C言語のように条件を丸括弧''()''で囲む必要はないことに注意してください。 
-囲んでもエラーではありませんが、冗長なだけで何も良いことはありません。 
- 
-基本的に、括弧''{}''を省略することはできません。 
-しかし、実行されるブロック本体が1行である場合、括弧''{}''の代わり''do''を使うことができます。 
- 
-<file odin> 
-if 条件文 do fmt.println("yah") // 条件が真のとき実行される 
-else { 
- // ... 
-} 
-</file> 
- 
-''if''に、初期化文を含めることができます。 
- 
-<file odin> 
-if 初期化文; 条件文 { 
- // ... 
-} 
-</file> 
- 
-初期化文で宣言された変数は、''if''のブロック本体や、後続の''else if''の条件文、後続のブロック本体でのみ有効になります。 
-変数のスコープをできるだけ短くすることは良い習慣であるので、可能なら積極的に利用していくのが良いです。 
- 
-<file odin> 
-if answer := get_answer(); answer == 42 { 
- fmt.println("hi") 
-} else if answer < 0 {               // ここでもanswerが使える (また、このifに追加の初期化文を加えることも可能) 
- fmt.println("oh no!") 
-} else { 
- fmt.println("huh? ", answer)     // ここでもanswerが使える 
-} 
- 
-fmt.println(answer) // コンパイルエラー! 上のifを抜けたらanswerを使うことができない 
-</file> 
- 
- 
-===== switch文 ===== 
- 
-条件によって処理を分岐する、もう一つの方法として、switch文があります。 
-switch文は、条件を判定する対象が、ある一つのもの(主に変数)に限られます。 
-そのため、柔軟性はif文よりも低いですが、その代わりに記述が簡潔になり、コードの見た目も意図もスッキリして明らかになります。 
-適用できる場面ではswitch文の方を優先したほうが良いでしょう。 
-また、C言語のswitch文と比べれば、遥かに要件が緩和されているので、きっと使いやすいと感じられるかと思います。 
- 
-<file odin> 
-x := -100 // この値を変えてやれば出力が変わる 
-switch x { 
-case -100..<0: 
- fmt.println("-100を含めてそれより大きく、0より小さい") 
-case 0: 
- fmt.println("ゼロに等しい") 
-case 1, 2, 3: 
- fmt.println("1か2か3のいずれかに等しい") 
-case: 
- fmt.println("条件なしのケースはデフォルトケース") 
-} 
-</file> 
- 
-上の例で -100..<0 のところとは注目に値します。 
-このように、範囲を指定したケースを記述することができます。 
-1と2と3を同時に扱える点もまた注目に値します。 
- 
-間違って条件が重複するケースを書いてしまった場合、多くの場合はコンパイラによってコンパイルエラーとして検出されます。 
-例えば ''case -100..<0'' を ''case -100..='' としてしまった場合、0の場合が重複するのでコンパイルされません。 
-これはプログラミングエラーを検出するのに大きな役割をはてしてくれます。 
- 
-if文と同じように、switch文にも初期化文を含めることができます。 
-直前の例は、次のように書き換えることができます。 
- 
-<file odin> 
-switch x := -100; x { 
-// ... 
-} 
-</file> 
- 
-少し前に、switch文は条件を判定する対象が、ある一つもの(主に変数)に限られる、と言いましたが、これには嘘が含まれています。 
-switch文の条件は完全に取り除くことが可能です。 
-この場合、caseに任意の条件を書くことが可能となり、if文と同様の柔軟性が得られます。 
- 
-<file odin> 
-x := 10 
-y := 20 
-switch { 
-case x < 10: 
- fmt.println("x < 10") 
-case y < 20: 
- fmt.println("y < 20") 
-case x == 10 && y == 20: 
- fmt.println("wow") 
-} 
-</file> 
- 
-このように、Odinのswitch文はとても使い勝手が良いです。 
-一つ、気に留めておいたほうが良いことがあります。 
-switch文のよくある使い方として、enum型の変数によって処理を振り分けるというのがあります。 
- 
-<file odin> 
-State :: enum { 
- TitleMenu, 
- Playing, 
- Paused, 
- GameOver, 
-} 
-</file> 
- 
-まだenumは扱っていませんが、C言語などを知っていれば大体想像される通りのものです。 
-このStateというenum型の変数について、switch文で処理を振り分けたいとします。 
-そして、TitleMenuとPlayingのときだけ処理を行い、他は無視したいとします。 
- 
-<file odin> 
-current_state := State.Playing 
-switch current_state { 
-case .TitleMenu: 
- // 処理を行う... 
-case .Playing: 
- // 処理を行う... 
-case: 
- // 他は無視したい... 
-} 
-// コンパイルエラー! 
-</file> 
- 
-これはコンパイルされません。 
-エラー内容は、対応していないケースがある、言い換えれば、すべてのケースを網羅していないからというものです。 
-デフォルトケースを記述しているにも関わらずです。 
-これは奇妙に思われるかもしれません。 
-enumとswitch文のパターンのルールは少しだけ込み入ったものになっています((参考: https://github.com/odin-lang/Odin/discussions/2342))。 
-簡単にいうと、enumをswitch文で処理する場合、必ず全てのケースを「明示的に」取り扱わないといけません。 
-(直前の例のように)もしそうでないなら、''#partial switch''という特殊なswitchを用いる必要があります。 
- 
-修正自体は簡単です。 
- 
-<file odin> 
-current_state := State.Playing 
-#partial switch current_state { 
-case .TitleMenu: 
- // 処理を行う... 
-case .Playing: 
- // 処理を行う... 
-case: 
- // 他は無視したい... 
-} 
-// OK! 
-</file> 
- 
-これが必要となるケースは意外と多いです。 
-しかし、enumのメンバーを追加したときに、switch文のケースを追加せずともコンパイルをパスしてしまうという脆弱さが生まれてしまうので、注意が必要なところでもあります。 
- 
-===== for文 ===== 
- 
-Odinでループを表現するには、for文を使います。 
-従来の多くのC言語族にあるwhile文は存在しません。 
-forの条件によって、whileと同等のことが可能です。 
- 
-最も基本的なのは、初期化、条件、更新 のパターンのforです。 
-C言語のforに相当する、典型的なパターンのfor文です。 
- 
-<file odin> 
-for i := 0; i < 5; i += 1 { 
- fmt.println("loop: ", i) 
-} 
-</file> 
- 
-出力は次のようになります。 
- 
-  loop:  0 
-  loop:  1 
-  loop:  2 
-  loop:  3 
-  loop:  4 
- 
-必要のない部分は空にすることができます。 
- 
-<file odin> 
-i := 0 
-for ; i < 5; { 
- i += 1 
-} 
-fmt.println("after finished loop ", i) 
-</file> 
- 
-出力: 
- 
-  after finished loop  5 
- 
-この場合、機能的にはC言語のwhile文と同等です。 
-しかし、空の文が(セミコロン)が見苦しいです。 
-これは完全に取り除くことができて、そうすることによって、C言語のwhile文と同等なものとなります。 
- 
-<file odin> 
-i := 0 
-for i < 5 { 
- i += 1 
-} 
-fmt.println("after finished loop ", i) 
-</file> 
- 
-出力: 
- 
-  after finished loop  5 
- 
-さらに条件すら取り除いてしまえば無限ループになります。 
- 
-<file odin> 
-for { 
- fmt.println("無限ループ中... Ctrl+C で停止") 
-} 
-</file> 
- 
-中途半端に取り除くことはできません。 
- 
-<file odin> 
-for i := 0; i < 5 {  // コンパイルエラー!  
- // ... 
-} 
-</file> 
- 
- 
-この伝統的なC言語スタイルのfor文は、まだまだ現役であるものの、範囲ベースのforループにかなり多くその役割を受け渡しています。 
- 
-範囲ベースforループは基本的に次のような形をしています。 
- 
-<file odin> 
-for 変数名 in 反復可能なオブジェクト { 
- // 変数名とした変数を使って処理を行う... 
-} 
-</file> 
- 
-先の数値を0から5の一つ手前までインクリメントしながらループする場合、次のようにすることができます。 
- 
-<file odin> 
-for i in 0..<5 { 
- fmt.println("loop ", i) 
-} 
-</file> 
- 
-出力: 
- 
-  loop:  0 
-  loop:  1 
-  loop:  2 
-  loop:  3 
-  loop:  4 
- 
-ここで5が含まれていないことに注意してください。 
-もし5も含めたい場合 ''0..=5'' とします。 
- 
-<file odin> 
-for i in 0..=5 { 
- fmt.println("loop ", i) 
-} 
-</file> 
- 
-出力: 
- 
-  loop:  0 
-  loop:  1 
-  loop:  2 
-  loop:  3 
-  loop:  4 
-  loop:  5 
- 
-反復可能なオブジェクトには色々とあります。 
-組み込み型のオブジェクトでは、以下のようになります。 
- 
-  * 文字列 
-  * 配列 
-  * スライス 
-  * 動的配列 
-  * マップ 
- 
-また、上で「変数名」としたところ「変数名1, 変数名2」のようにすることが可能な、柔軟な反復可能なオブジェクトもあります。 
- 
-<file odin> 
-xs := []int{100, 200, 300} 
-for value, index in xs { 
- fmt.println("value = ", value, ", index = ", index) 
-} 
-</file> 
- 
-出力: 
- 
-  value =  100 , index =  0 
-  value =  200 , index =  1 
-  value =  300 , index =  2 
- 
-この例は、反復可能なオブジェクトとして、スライスを使ったものです。 
-C言語スタイルのforループよりも、より間違いにくく、読みやすいので、可能ならば範囲ベースのforを優先して使っていくことになります。 
- 
-範囲ベースforについて、もう一点付け加えておきます。 
-「変数名」としたところにバインドされるのは、値のコピーです。 
-これを参照にしたいことは多くあります。 
-Odinでもそのようなことは当然考えられていて、参照をバインドしてループすることが可能です。 
-それによって、反復元のオブジェクトの値を書き換えることが可能です。 
- 
-<file odin> 
-xs := []int{100, 200, 300} 
-for &value in xs { 
- value += 10 
-} 
-fmt.println(xs) 
-</file> 
- 
-出力: 
- 
-  [110, 210, 310] 
- 
-範囲ベースforについては以上です。 
- 
-他の反復可能なオブジェクトについては、少し先で取り扱う予定なのでそれまで先送りしておきます。 
- 
-===== do ... while は存在しない ===== 
- 
- 
-ループの末尾で条件判定を行うループ (C言語の do .. while に相当するもの) はありません。 
-もし必要なら、break文を利用してなんとかする必要があります。 
- 
-例: 
- 
-<code odin> 
-// "yes"か"no"のいずれかが入力されるまで、ユーザーに入力を求める 
- 
-answer: string 
-for { 
- // もう一度ユーザーから入力を受け付ける 
- answer = read_user_input(prompt = "続けますか? (yes/no)") 
-  if answer == "yes" || answer == "no" { 
- break 
- } else { 
- fmt.println("yesかnoを入力してください。") 
- } 
-} 
-</code> 
- 
-少し不格好かもしれませんが、たいていの目的は達成できます。 
-===== defer文 ===== 
- 
- 
-===== when文 ===== 
- 
- 
-===== break文、continue文、fallthrough文 ===== 
- 
  
文書の先頭へ