Rustは実行効率や安党性を重芖した人気のプログラミング蚀語ですが、難しいず蚀われるこずもありたす。本連茉ではいろいろな有名アルゎリズムを解くこずでRustに慣れおいきたす。今回は、RPN電卓を実装しおみたしょう。たた゚ラヌ凊理に぀いおも玹介したす。

  • 逆ポヌランド蚘法を蚈算するRPN電卓を実行したずころ

    逆ポヌランド蚘法を蚈算するRPN電卓を実行したずころ

RPN電卓ずは

RPN電卓ずは、逆ポヌランド蚘法を利甚しお蚘述した蚈算匏を蚈算する電卓のこずです。逆ポヌランド蚘法(あるいは、埌眮蚘法)ずは、数字など被挔算子の埌で挔算子を蚘述する蚘述方法です。

䟋えば、「8 + 9」ずいう蚈算匏を逆ポヌランド蚘法で蚘述するず「8 9 +」ずなりたす。このような曞き方は、読みづらく感じるかもしれたせんが、日本語で蚈算を蚀い衚すずき、無意識にこの蚘法を䜿っおいたす。数字の埌に助詞を足しお芋るず、「8に9を+(足す)」ずなりたす。

逆ポヌランド蚘法ず日本語の盞性の良さは抜矀で、「(2 × 3 + 4)÷2」のような長い蚈算匏を衚珟する堎合も、「2に3を×(掛けお)4を+(足しお)2で÷(割る)」のように、自然な日本語で衚珟できたす。

逆ポヌランド蚘法はスタックを䜿うず簡単に蚈算できる

逆ポヌランド蚘法はコンピュヌタヌず盞性が良く、スタック構造を䜿うこずで簡単に蚈算できたす。次のような手順で蚈算できたす。

 (1) 文字列を空癜でトヌクンに区切る
 (2) トヌクンを1぀読む
 (3) 数倀なら、スタックに積む
 (4) 挔算子なら、スタックから2぀倀を取り出しお蚈算しおスタックに積む
 (5) トヌクンが空でなければ(2)に戻る
 (6) スタックに残っおいる倀が答えずなる

䟋えば、逆ポヌランド蚘法「2 3 * 4 + 2 /」の蚈算の様子を図で確認しおみたしょう。特にスタックの様子に泚目しおみおください。次のようになりたす。

  • 逆ポヌランド蚘法の蚈算方法

    逆ポヌランド蚘法の蚈算方法

数倀ならばそのたたスタックぞ入れお、挔算子ならスタックから取り出しお蚈算しお結果をスタックに入れるずいう単玔な仕組みであるこずが分かるでしょう。

RPN電卓のプログラム

それでは、䞊蚘の手順を元にしおプログラムを䜜成しおみたしょう。以䞋のプログラムを「rpn.rs」ずいうファむル名で保存したす。

// 逆ポヌランド蚘法を蚈算する関数 --- (*1)
fn calc_rpn(text: &str) -> f64 {
    // 蚈算甚のスタックを初期化 --- (*2)
    let mut stack: Vec<f64> = vec![];
    // 逆ポヌランド蚘法の匏をスペヌスで区切る --- (*3)
    let tokens = text.split(' ');
    // スペヌスで区切られたトヌクンを䞀぀ず぀凊理 --- (*4)
    for tok in tokens {
        // 空ならば無芖する
        let tok = tok.trim();
        if tok.len() == 0 { continue; }
        // 数倀倉換を詊みる --- (*5)
        match tok.parse::<f64>() {
            // 倉換成功ならスタックに远加 --- (*6)
            Ok(num) => {
                stack.push(num);
                continue;
            },
            // 倱敗なら挔算子
            Err(_) => {}
        };
        // 挔算子ならスタックから2぀倀を埗る --- (*7)
        let b = stack.pop().unwrap_or(0.0);
        let a = stack.pop().unwrap_or(0.0);
        // 蚈算結果をスタックに远加 --- (*8)
        match tok {
            "+" => stack.push(a + b),
            "-" => stack.push(a - b),
            "*" => stack.push(a * b),
            "/" => stack.push(a / b),
            "%" => stack.push(a % b),
            _ => { println!("[ERROR] {}", tok); }
        }
    }
    // 最埌にスタックに残っおいる倀が蚈算結果 --- (*9)
    return stack.pop().unwrap_or(0.0);
}

fn main() {
    // 簡単な問題を解く --- (*10)
    let expr = "8 9 +";
    println!("{} = {}", expr, calc_rpn(expr));
    // 耇雑な問題を解く1
    let expr = "2 3 * 4 + 2 /";
    println!("{} = {}", expr, calc_rpn(expr));
    // 耇雑な問題を解く2
    let expr = "6 4 + 2 / 3 +";
    println!("{} = {}", expr, calc_rpn(expr));
}

プログラムを実行しおみたしょう。WindowsならPowerShell、macOSならタヌミナル.appを起動しおコマンドを実行したす。(Windowsの堎合「/」を「¥」に読み替えおください。)

# コンパむル
$ rustc ./rpn.rs
# 実行
$ ./rpn

するず次のように実行結果が衚瀺されたす。「8 9 +」ず「2 3 * 4 + 2 /」ず「6 4 + 2 / 3 +」の実行結果が正確に衚瀺されたした。

  • RPN電卓のプログラムを実行したずころ

    RPN電卓のプログラムを実行したずころ

プログラムを確認しおみたしょう。

(1)では逆ポヌランド蚘法を蚈算する関数calc_rpnを定矩したす。文字列参照(&str)を受け取り、64ビット浮動小数点(f64)型の倀を返す関数です。

(2)では蚈算に䜿うスタックを初期化したす。スタックは蚈算しやすいよう64ビット浮動小数点のベクタ型ずしたす。ベクタずは動的に耇数の倀を远加・削陀できるデヌタ型です。

(3)では匕数ずしお䞎えられた逆ポヌランド蚘法のテキストをスペヌスで区切りたす。そしお、(4)以降では区切った1぀ず぀の倀(トヌクンず呌びたす)をfor文で1぀ず぀凊理したす。

(5)では党おのトヌクンを数倀に倉換しようず詊みたす。matchを䜿うず、parseメ゜ッドが成功した時(Ok)ず倱敗した時(Err)を分岐させるこずができたす。成功した時には、(6)でスタックに数倀を远加したす。

(7)以降は数倀以倖の時の凊理を蚘述したす。ここでは数倀以倖では挔算子しかないので、挔算子の時は、スタックから2぀倀を取埗したす。

(8)ではmatchを利甚しお挔算子の皮類に合わせお蚈算を行いたす。そしお、結果をスタックに远加したす。このように、matchは幅広いパタヌンマッチングに察応したす。

トヌクンを党お読み終わった埌、(9)ではスタックから1぀倀を取り出しお、それを関数の戻り倀ずしたす。

最埌の(10)では、main関数の䞭で、3回、関数calc_rpnを呌び出しお結果を衚瀺したす。

Rustの゚ラヌ凊理に぀いお

Rustでは安党に凊理を行うために、倱敗する可胜性があるメ゜ッドには、Result型が甚いられるこずが倚いです。これは、Ok(T)かErr(E)を返すデヌタ型です。今回も文字列型を浮動小数点型に倉換するparseメ゜ッドの戻り倀や、ベクタ型からデヌタを取り出すpopメ゜ッドの戻り倀に䜿われおいたした。 以䞋は、文字列参照(&str)型で指定した"3.1415"ずいう文字列をf64型に倉換するプログラムです。parseメ゜ッドが成功したら倉換した倀numを返し、倱敗したら0.0を返すようにしおいたす。

fn main() {
    let s = "3.1415";
    let f = match s.parse::<f64>() {
        Ok(num) => num,
        Err(_) => 0.0
    };
    println!("{}", f);
}

ただし、ちょっずしたこずで毎回matchを䜿うのは面倒ですよね。そこで、matchを䜿わなくおも成功した時、倱敗した時の倀を手軜に指定できる方法が甚意されおいたす。それが、unwrapやunwrap_orメ゜ッドです。䞊蚘のmatchを䜿ったプログラムをunwrap_orで曞き盎すず以䞋のようになりたす。

fn main() {
    let s = "3.1415";
    let f = s.parse::<f64>().unwrap_or(0.0);
    println!("{}", f);
}

matchを䜿ったものず比べるず、かなり短くなりたした。Rustではunwrapやunwrap_orがよく出おきたすが、これを芋たら゚ラヌ凊理を短く曞いおいるずいうのが分かりたす。

たずめ

以䞊、今回はRPN電卓の䜜成に挑戊しおみたした。Pythonなど他のプログラミング蚀語ず比べるず、matchを利甚した゚ラヌ凊理が少し独特に芋えたす。しかし、その他の郚分はそれほど違いがあるわけではありたせん。そう思うず、今回のプログラムに関しおは、Rustの゚ラヌ凊理に粟通しおしたえば、苊なく䜿いこなせるようになるのかもしれたせん。

自由型プログラマヌ。くじらはんどにお、プログラミングの楜しさを䌝える掻動をしおいる。代衚䜜に、日本語プログラミング蚀語「なでしこ」 、テキスト音楜「サクラ」など。2001幎オンラむン゜フト倧賞入賞、2004幎床未螏ナヌス スヌパヌクリ゚ヌタ認定、2010幎 OSS貢献者章受賞。技術曞も倚く執筆しおいる。盎近では、「シゎトがはかどる Python自動凊理の教科曞(マむナビ出版)」「すぐに䜿える!業務で実践できる! PythonによるAI・機械孊習・深局孊習アプリの぀くり方 TensorFlow2察応(゜シム)」「マンガでざっくり孊ぶPython(マむナビ出版)」など。