2017年6月18日日曜日

The Rust Programming Language 2nd 2日目 Example: Guessing Game

https://doc.rust-lang.org/book/second-edition/
Apache License Version 2.0

2. Guessing Game

数あてゲームを作る.
bash

cargo new guessing_gaame --bin

で新しいプロジェクトを作成する.ランダムな整数を生成するのにrandクレートを使うので,Cargo.tomlにrandを追加する.クレートはrustコードのパッケージであって,randlibraryクレートと言って,他のプログラムで利用されるコードが入っている.

Cargo.toml

[dependencies]
rand = "0.3.14"

main.rs

use std::io;                 // 標準ライブラリのioを名前空間に追加.
                                        // std::io::stdin()をio::stdin()と省略できる.
use std::cmp::Ordering;
use rand::Rng;

fn main() {                            // Rustではfn <関数名>で関数を定義する.
                                       // Cと同じくmainが最初に実行される.

  println!("Guess the number!");        // 文字列をコンソールに書き出すマクロ.
                                        // !は関数でなくマクロを読んでいることを表している.
                                        // インデントは4スペースがいいらしいが色々と統一したいので2スペース
                                        // ;でexpressionの終わりを明示する.
                                        // Cなどの,行の終わりに;を打つ言語とはちょっと違うらしい(後述).

  let secret_number = rand::thread_rng().gen_range(1, 101); // thread_rng()がランダムなジェネレーターをつくる
                                                            // .gen_range(1,101)で範囲内の値で生成するようにする

  loop {                   // 中でbreakするまで繰り返す.カウンタを使わない分処理が早い
    println!("Please input your guess");

    let mut guess = String::new();      // 変数の宣言は'let'で行う. 'mut'によって変数guessを可変にする.
                                        // let foo=bar;などとすると,fooにはbarが代入されて以後変更できない
                                        // String::new()は長さが可変な文字列型Stringのassociated functionで,
                                        // 新しい空の文字列を作成する.

    io::stdin().read_line(&mut guess)   // 後述(1)
      .expect("Failed to read line");

    let guess: u32 = match guess.trim().parse() { // 後述 (2)
      Ok(num) => num,
      Err(_)  => continue,
    };

    println!("You guessed: {}", guess); // 変数を文字列に埋め込んで出力する方法はpythonと同じ

    match guess.cmp(&secret_number) {   // 後述 (3)
      Ordering::Less     => println!("Too small!"),
      Ordering::Greater  => println!("Too big!"),
      Ordering::Equal    => {
        println!("You win!");
        break;
      }
    }
  }
}

(1)

    io::stdin().read_line(&mut guess)   // 後述(1)
      .expect("Failed to read line");

io::stdin()は標準入出力のhandleのインスタンスを返す.そのインスタンスのメソッドread_lineが入力を読み取り,guessに文字列として代入する.代入先の変数は可変でなければならない.&はこの引数がreferenceであることを表していて,いちいち新しくメモリを割り当てさせない.referenceはChap.4で詳しく論じる.
一行目で行われた処理はguessに値を代入することだが,read_line()はio::Reuslt型の返り値を返す.Result型は’Ok’か’Err’の値しか取らない.’Ok’は処理が正常に行われたとき返され,’Err’はそうでないときに返される.Result型のexpectメソッドによって,実行時のインスタンスがErrであったときはexpectの引数である文字列を表示し,プログラムはクラッシュする.Resultのインスタンスにexpectをつけないとコンパイル時に警告される.

(2)

    let guess: u32 = match guess.trim().parse() { // 後述 (2)
      Ok(num) => num,
      Err(_)  => continue,
    };

let <変数名> :<変数型>によって,型を指定して変数を定義できる.
新しい変数guessをu32型として定義して,もとあったguessを数値に変換した値を新しいguessに代入する.(このように,同じ名前の変数を定義して,もとあった変数を加工した値を代入することをshadowingという.)
trim()メソッドによって文字列の始まりと終わりにある空白を削除し,更にparse()によって改行記号があればそれを削除する.parse()が正常に機能したか否かをmatch expressionによって条件分岐し,正常な場合は新しいguessに,古いguessを数値として代入する.異常な場合はcontinueによって直ちに次にループを実行する.
Err()の’‘はcatchall記号で,エラーの詳細が何であっても,Errならこれに一致する.

match A { B => b, C => c, D => d, ...};

は, A がXに等しいときxを返したり実行したりする.

(3)

    match guess.cmp(&secret_number) {   // 後述 (3)
      Ordering::Less     => println!("Too small!"),
      Ordering::Greater  => println!("Too big!"),
      Ordering::Equal    => {
        println!("You win!");
        break;
      }

cmpメソッドはもとのインスタンスと引数の大小比較を行い,std::cmp::Ordering型のインスタンス{Less,Greater,Equal}のどれかを返す.返されたインスタンスが何であるかによる条件分岐をmatch expressionで実現する.

0 件のコメント:

コメントを投稿