はじめに
Rustの基本的な関数3つについて、RPN計算機プログラムを見ながら解釈していく。
リファレンス
実践Rust入門 言語仕様から開発手法まで
URL(amazon): https://www.amazon.co.jp/%E5%AE%9F%E8%B7%B5Rust%E5%85%A5%E9%96%80-%E8%A8%80%E8%AA%9E%E4%BB%95%E6%A7%98%E3%81%8B%E3%82%89%E9%96%8B%E7%99%BA%E6%89%8B%E6%B3%95%E3%81%BE%E3%81%A7-%CE%BAeen/dp/4297105594
URL(楽天): https://books.rakuten.co.jp/rb/15856840/
著者本人のブログ
κeenのHappy Hacκing Blog | Lispエイリアンの狂想曲
RPNについて
RPNとは__"Reverse Polish Notation"の略で、日本では"逆ポーランド記法"__と呼ばれる。(基本情報にでてくるらしい。)
僕自身は、「かっこ」のイメージでとらえている。
例えば、
11 + 22
は、
11 22 +
と表記する。+の値がべ手の四則計算記号になっても同様である。
少し応用。
11 22 * 33 +
はいつもどうりに直すと、
11 * 22 + 33
となる。
もとの式をかっこを付けてわかりやすくしてみると
{(11 22) * 33 }+
という風にみえる。自分なりの解釈の仕方でいいと思う。
これを基にすると、
11*(22+33)
という普通式は、
11 22 33 + * ⇐ { 11 ( 22 33 ) + } *
となる。
RPN計算機プログラム ソースコード
from https://github.com/ghmagazine/rustbook/blob/master/ch02/rpn/src/main.rs
上記記載のコードは、日本語による説明あり。
file name == main_function
fn main() {
let exp = "13.3 6.3 3.9 * + 3.7 2.8 / 9.1 * -";
let ans = rpn(exp);
debug_assert_eq!("25.8450", format!("{:.4}", ans));
println!("{} = {:.4}", exp, ans);
}
fn rpn(exp: &str) -> f64 {
let mut stack = Vec::new();
for token in exp.split_whitespace() {
if let Ok(num) = token.parse::<f64>() {
stack.push(num);
} else {
match token {
"+" => apply2(&mut stack, |x, y| x + y),
"-" => apply2(&mut stack, |x, y| x - y),
"*" => apply2(&mut stack, |x, y| x * y),
"/" => apply2(&mut stack, |x, y| x / y),
_ => panic!("Unknown operator: {}", token),
}
}
}
stack.pop().expect("Stack underflow")
}
fn apply2<F>(stack: &mut Vec<f64>, fun: F)
where
F: Fn(f64, f64) -> f64,
{
if let (Some(y), Some(x)) = (stack.pop(), stack.pop()) {
let z = fun(x, y);
stack.push(z);
} else {
panic!("Stack underflow");
}
}
}
ソースコード解釈(概要)
fn main()
で、メイン関数が定義される。
その中身を今回は
13.3 6.3 3.9 * + 3.7 2.8 / 9.1 * -
という式の内容を計算する。
これの答えは、
25.8450
となるはずである。
__let__により新しい変数の用意をする。すなわち、値に新しい名前を付ける。
ここで、__main関数のみで実行してみる__と、
error[E0425]: cannot find function `rpn` in this scope
--> main_function.rs:3:15
|
3 | let ans = rpn(exp);
| ^^^ not found in this scope
error: aborting due to previous error
For more information about this error, try `rustc --explain E0425`.
と出てくる。「rpnに関する定義がない。」となるので、その定義となる__rpn関数__が必要となる。
なので、
fn rpn()
その中身が以下のようになっている。
fn rpn(exp: &str) -> f64 {
let mut stack = Vec::new();
for token in exp.split_whitespace() {
if let Ok(num) = token.parse::<f64>() {
stack.push(num);
} else {
match token {
"+" => apply2(&mut stack, |x, y| x + y),
"-" => apply2(&mut stack, |x, y| x - y),
"*" => apply2(&mut stack, |x, y| x * y),
"/" => apply2(&mut stack, |x, y| x / y),
_ => panic!("Unknown operator: {}", token),
}
}
}
stack.pop().expect("Stack underflow")
}
という、関数の塊ができる。f64型(64ビット浮動小数点数)で定義しているので、もっと多くの2進数計算となる場合、ビット数は増やさなければならない。
apply2
というものに関して、定義されているものはソースコードの塊で一番下の__apply2関数__によって定義される。
よって
fn apply2()
が
fn apply2<F>(stack: &mut Vec<f64>, fun: F)
where
F: Fn(f64, f64) -> f64,
{
if let (Some(y), Some(x)) = (stack.pop(), stack.pop()) {
let z = fun(x, y);
stack.push(z);
} else {
panic!("Stack underflow");
}
}
}
というように定義されている。
中身に関しては、github(URL上記記載)での説明を見ればわかる。
これで、大枠のことがわかる。
構造自体は、C/C++に似ているので、非常にわからいやすい。
最後に
rustに関する記事は、様々なものを調べていながら、Errorを発生させてみながら、学んでみる。Error内容にも強くなっていきたい。または、もらいたい。