2017年6月20日火曜日

The Rust Programming Language 2nd 3日目 変数,基本型,関数

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

Common Programing Concepts

多くの言語に共通する機能がRustではどう実現されているか

3.1 Variables and Mutability

bash

cargo new --bin variables

で実験するプロジェクトを作る.
Rustでは,変数はデフォルトではimmutableである.つまり,一度変数に値を代入するとその後変更できない.

src/main.rs

fn main() {
  let x = 5;
  println!("The value of x is: {}", x);
  x = 6;
  println!("The value of x is: {}", x);
}

cargo runでビルドして実行してみると,

error[E0384]: re-assignment of immutable variable `x`
 --> src/main.rs:4:5
  |
2 |     let x = 5;
  |         - first assignment to `x`
3 |     println!("The value of x is: {}", x);
4 |     x = 6;
  |     ^^^^^ re-assignment of immutable variable

とエラーが出る.こうして,Rustはプログラマが予期しない代入を防いでいる.

しかし,いちいち新しい変数を用意するのは不便なときもある.そこで
src/main.rs

fn main() {
  let mut x = 5;
  println!("The value of x is: {}", x);
  x = 6;
  println!("The value of x is: {}", x);
}

とすると,

   Compiling variables v0.1.0 (file:///home/ren/Projects/variables)
    Finished dev [unoptimized + debuginfo] target(s) in 0.34 secs
     Running `target/debug/variables`
The value of x is: 5
The value of x is: 6

このようにletによる変数宣言と同時にmutによって変数をmutableにすることが出来る.

3.2 Differences Between Variables and Constants

定数とimmutableな変数は似ているが,いくつか違いがある.
1. immutable変数はmutによってmutableにできる.
2. 定数にはプログラムのどこからでも参照できる.
3. 定数はプログラム実行時に計算される値は代入できない.つまり,コンパイル時に決定される値しか代入できない.

Shadowing

新しい変数をすでにある変数と同じ名前で宣言することをshadowingという.このとき,新しい変数ははじめの変数をshadowしているといい,はじめの変数は新たな変数にshadowされたという.

src/main.rs

fn main() {
  let x = 5;
  let x = x + 1; // shadow 1回目
  let x = 2 * x; // shadow 2回目

  println!("The value of x is: {}", x);
}

shell

ren@ren-ThinkCentre-Edge72:~/Projects/variables$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
     Running `target/debug/variables`
The value of x is: 12

mutableな変数でも,宣言したあとで型を変えることはできない(静的型付).

src/main.rs

fn main() {
  let mut spaces = "  "; // 右辺は文字列
  spaces = spces.len();  // 右辺は整数の2
}

shell

ren@ren-ThinkCentre-Edge72:~/Projects/variables$ vim src/main.rs

   Compiling variables v0.1.0 (file:///home/ren/Projects/variables)
error[E0308]: mismatched types
 --> src/main.rs:3:14
  |
3 |     spaces = spaces.len()
  |              ^^^^^^^^^^^^ expected &str, found usize
  |
  = note: expected type `&str`
             found type `usize`

変数名を型を変えながら使いまわしたいとき,shadowingが有効.

Data Types

Rustは静的型付言語だが,変数宣言時に型を指定しなくても,コンパイラが推測してくれる.しかし,プログラマが型を指定しなければならない場合もある.

Scalar Types

整数,浮動小数点数,真理値,文字は長さが固定されている変数であり,scalarという.

Integer Types (自然数型)

自然数型は符号のあるなしとビット長によっての10種類がある.ビット長は8, 16, 32, 64, そしてアーキテクチャ依存のsize長がある.符号付きならi,符号なしならuを頭につけて,ビット長を尾につけてその型を指定できる.例えば符号なし32bitなら u32 である. Rustのデフォルトはi32で,64bit CPUでもこれが最も早く処理できることが多い.

src/main.rs

fn main() {
  let x:i32 = 2147483648;     // i32だとオーバーフローする値
  println!("x is {}", x);
}

shell

Compiling variables v0.1.0 (file:///home/ren/Projects/variables)
warning: literal out of range for i32
--> src/main.rs:2:13
|
2 |     let x = 2147483648;
|             ^^^^^^^^^^
|
= note: #[warn(overflowing_literals)] on by default

 Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs
  Running `target/debug/variables`
x is -2147483648 

オーバーフローする値を代入しても,オーバーフローしないような型にしてくれるわけではない.コンパイルエラーにはならず,コンパイル時に警告される.

Floating-Point Types (浮動小数点数型)

小数点以下が表現できる.32bit長(f32)と64bit長(f64)があるが,速さはほとんど同じなので64bit長がよく使われる.

Numeric Operations

算術的演算はpythonと同じ記法で行える.しかし,自然数と浮動小数点数の演算を直接行うことはできない.

src/main.rs

fn main() {
  let a = 3;
  let b = 3.0;
  let c = a + b;
  println!("int plus float: {}", c);
}

shell

Compiling variables v0.1.0 (file:///home/ren/Projects/variables)
error[E0277]: the trait bound `{integer}: std::ops::Add<{float}>` is not satisfied
--> src/main.rs:4:13
|
4 |     let c = a + b;
|             ^^^^^ the trait `std::ops::Add<{float}>` is not implemented for `{integer}`
|
= note: no implementation for `{integer} + {float}`

error: aborting due to previous error

error: Could not compile `variables`.

To learn more, run the command again with --verbose.

The Boolean Type

真理値はboolによって指定できて,truefalseの値だけを取る.
型を指定せずとも,truefalseを宣言時に代入すれば,コンパイラがbool型と推測する.

The Character Type

1文字を表現できるスカラー型はcharで,内部ではUnicodeを保持する.

Compound Types

長さが2以上ある変数の型.tuplearrayがある.

Grouping Values into Tuples

必ずしも共通でない型の値たちを一つの変数に代入する型.カッコでくくり,コンマで要素を区切る.タプル定義でも型推測は行われるが,プログラマが型を指定することも出来る.タプルの要素を取り出す方法は2つある.
1. pattern matching: タプルの長さと同じ個数の変数にタプルを代入し,求めたい要素の入っている変数にアクセスする
2. index: タプルの一番左の要素を0として,求めたい変数のインデックスがiなら,tupple.iでアクセスできる.

src/main.rs

fn main() {
  let tup = (500, 6.4, 1);
  let typed_tup: (i32, f64, u8) = (500, 6.4, 1);
  let (x, y, z ) = tup;
  println!("The value of y is: {}", y);           // pattern matchingによるアクセス

  println!("The value of y is: {}", typed_tup.1); // indexによるアクセス
}

shell

Compiling variables v0.1.0 (file:///home/ren/Projects/variables)
 ~ 略 (一度も使っていない変数があると怒られる) ~ 
Finished dev [unoptimized + debuginfo] target(s) in 0.43 secs
 Running `target/debug/variables`
The value of y is: 6.4
The value of y is: 6.4

Arrays

tuppleと違って,すべての要素が同じ型でなければならない.また,長さは固定である.定義するときは’[ ]’でくくって、コンマで要素を区切る.arrayもtuppleのようなインデックスが振られているが,アクセスするにはarray[i]と書く.arrayの長さに反するインデックスでアクセスしようとすると,コンパイルエラーは起きないが実行時にpanicする.

src/main.rs

fn main() {
  let a = [1, 2, 3, 4, 5];
  let index = 10;
  let element = a[index];

  println!("The value of element is: {}", element);
}

shell

Compiling variables v0.1.0 (file:///home/ren/Projects/variables)
 Finished dev [unoptimized + debuginfo] target(s) in 0.33 secs
  Running `target/debug/variables`
thread 'main' panicked at 'index out of bounds: the len is 5 but the index is 10', src/main.rs:4
note: Run with `RUST_BACKTRACE=1` for a backtrace.

3.3 How Functions Work

新しくfunctionsプロジェクトを作る.
関数はfn <関数名>() {}で定義され,{}の中身が実際に実行される.2つ以上関数をつくって,片方からもう片方の関数を利用することが出来る.

src/main.rs

fn main() {
  println!("Hello, world!");

  another_function();
}  

fn another_function() {
  println!("Another function.");
}

shell

Compiling functions v0.1.0 (file:///home/ren/Projects/functions)
 Finished dev [unoptimized + debuginfo] target(s) in 0.36 secs
  Running `target/debug/functions`
Hello, world!
Another function.

Function Parameters

関数の引数は,fn name () {}()の中で指定する.引数の名前だけでなく,型も同時に指定しなければならない.

src/main.rs

fn main() {
  another_function(1, 2);
}

fn another_function(x:i32, y) {  // yの型を指定していない.
  println!("x is {}", x);
  println!("y is {}", y);
}

shell

Compiling functions v0.1.0 (file:///home/ren/Projects/functions)
error: expected one of `:` or `@`, found `)`
--> src/main.rs:6:29
|
6 | fn another_function(x:i32, y) {
|                             ^ expected one of `:` or `@` here

error[E0425]: cannot find value `y` in this scope
--> src/main.rs:8:25
|
8 |     println!("y is {}", y);
|                         ^ did you mean `x`?

error: aborting due to 2 previous errors

error: Could not compile `functions`.

Statements and Expressions

Rustはepxressionベースの言語である.statementとexpressionの違いは,後者が返り値を返すが,前者はそうでないという点である.
変数宣言や関数定義はstatementであるから,返り値を返さない.よって

let x = (let y = 6);

はコンパイルできない.
一方で,expressionはプログラムの大半を占めている.

fn main() {
  let x = 5;      // 5自体はexpressionで,5を返す. expressionはstatementの一部になれる.

  let y = {
    let x = 3;    
    x + 1         
  };              // {}はexpression

  println!("The value of y is: {}", y);  // マクロはexpression
}

最後に;のない行はexpressionであり,;を加えるとstatementになる.

Functions with Return Values

関数はfn name () -> type {}によって返り値を返すようにすることが出来る.返される値は,{}の中の最後のexpressionの値である.
src/main.rs

fn five() -> i32 {
  4;   // このステートメントは特に何もしない.
  5    // これが{}の中の最後のexpressionだから,これの返り値がfive()の返り値になる.
}

fn main() {
  let x = five();
  println!("The value of x is: {}", x);
}

shell

Compiling functions v0.1.0 (file:///home/ren/Projects/functions)
 Finished dev [unoptimized + debuginfo] target(s) in 0.34 secs
  Running `target/debug/functions`
The value of x is: 5

返り値が返されると定めた関数にepxressionがない場合,コンパイルエラーになる.

src/main.rs

fn plus_one(x:i32) -> i32 {
  x + 1;
}

fn main() {
  let x = plus_one(5);
  println!("The value of x  is: {}", x);
}

shell

Compiling functions v0.1.0 (file:///home/ren/Projects/functions)
error[E0308]: mismatched types
--> src/main.rs:1:27
|
1 |   fn plus_one(x:i32) -> i32 {
|  ___________________________^
2 | |   
3 | |   x + 1;
4 | | }
| |_^ expected (), found i32
|
= note: expected type `()`
          found type `i32`
help: consider removing this semicolon:
--> src/main.rs:3:8
|
3 |   x + 1;
|        ^

error: aborting due to previous error

error: Could not compile `functions`.

To learn more, run the command again with --verbose.

Control Flow

条件分岐とループ

if expression

if condition_1 {codeblock_1} else if condition_2 {codeblock_2} … else {codeblock_0}
condition_1から順にconditionがtrueか調べられ,condition_kがtrueならcodeblock_kが実行され,すべてのconditionがfalseなら,codeblock_0が実行される.elseがない場合,何も実行されない.codeblockたちをifのarmと呼ぶことがある.conditionは,評価するとBoolを返すexpressionでなければならない.

src/main.rs

fn main() {
  let number = 3;
  if number < 5 {
    println!("condition was true");
  } else {
    println!("condition was false");
  }
}

shell

Compiling branches v0.1.0 (file:///home/ren/Projects/branches)
 Finished dev [unoptimized + debuginfo] target(s) in 0.35 secs
  Running `target/debug/branches`
condition was true

Using if in a let statement

ifはexpressionなので,letを使った変数宣言の右辺に置くことが出来る.
src/main.rs

fn main() {
  let condition = true;
  let number = if condition {
    5
  } else {
    6
  };

  println!("The value of number is: {}", number);
}

shell

Compiling branches v0.1.0 (file:///home/ren/Projects/branches)
 Finished dev [unoptimized + debuginfo] target(s) in 0.34 secs
  Running `target/debug/branches`
The value of number is: 5

ここで,変数numberにはconditionの真偽によって{5}, {6}のコードブロックの返り値が代入されるが,このコードブロックたちの返す値の型が異なっていると,numberの型が推測できず,コンパイルエラーになる.
src/main.rs

fn main() {
  let condition = true;
  let number = if condition {
    5
  } else {
    "six"
  };

  println!("The value of number is: {}", number);
}

shell

Compiling branches v0.1.0 (file:///home/ren/Projects/branches)
error[E0308]: if and else have incompatible types
--> src/main.rs:3:16
|
3 |     let number = if condition {
|  ________________^
4 | |       5
5 | |     } else {
6 | |         "six"
7 | |       };
| |_______^ expected integral variable, found &str
|
= note: expected type `{integer}`
          found type `&str`

error: aborting due to previous error

error: Could not compile `branches`.

Repetition with Loops

Rustはloop,while,forの3種類のループを実装している.

Repeating Code with loop

明示的にbreakされるまでループを続ける.

src/main.rs

fn main() {
  loop {
    println!("again!");
  }
}

shell

again!  # 無限ループ

Conditional Loops with while

ループ中毎回条件を評価し,条件がfalseになったらループを中断する.
src/main.rs

fn main() {
  let mut number = 3;
  while number != 0 {
    println!("{}", number);
    number = number - 1;
  }
  println!("LIFTOFF!!!");
}

shell

Compiling branches v0.1.0 (file:///home/ren/Projects/branches)
 Finished dev [unoptimized + debuginfo] target(s) in 0.35 secs
  Running `target/debug/branches`
3
2
1
LIFTOFF!!!

Looping Through a Collection with for

tupplearrayの要素ごとに処理を実行したいとき,forが使える.また,決まった回数ループしたい場合,Range型にforを使って実現できる.

src/main.rs

fn main() {
  for number in (1..4).rev() { 
    println!("{}!", number)
  }
  println!("LIFTOFF!!!");
}

shell

Compiling branches v0.1.0 (file:///home/ren/Projects/branches)
 Finished dev [unoptimized + debuginfo] target(s) in 0.38 secs
  Running `target/debug/branches`
3!
2!
1!
LIFTOFF!!!

Summary (練習)
1. 華氏を摂氏に変換するプログラムを書け.
2. n番目のフィボナッチ数を計算するプログラムを書け

  1. src/main.rs
use std::io;

fn main() {
  println!("Input F temperature!");
  let mut f = String::new();
  io::stdin().read_line(&mut f).expect("Failed to read line.");
  let f: f64 = f.trim().parse().expect("Please type a number!");

  let c = (f - 32.0) * 5.0 / 9.0;
  println!("Fahrenheit {} is Celsius {}", f, c);
}

shell

Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
 Running `target/debug/branches`
Input F temperature!
911
Fahrenheit 911 is Celsius 488.3333333333333
  1. src/main.rs
use std::io;

fn main() {
  println!("Input integer n!");
  let mut  n = String::new();
  io::stdin().read_line(&mut n).expect("Failed to read line.");
  let n: u32 = n.trim().parse().expect("Plase type an intger!");

  let ans = fibb(n);
  println!("The kth fibbonacci number is: {}", ans);

}

fn fibb(k: u32) -> u32 {
  let ans:u32 = if k == 1 {
   1 } else if k == 2 {
   1 } else { fibb(k-1) + fibb(k-2)};

  ans
}

shell

Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
 Running `target/debug/branches`
Input integer n!
12
The kth fibbonacci number is: 144

0 件のコメント:

コメントを投稿