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