Rubyで逆ポーランド変換機を作りgem作成&コマンドの使い方:若手エンジニア/初心者のためのRuby 2.1入門(13)(3/4 ページ)
オープンソースのオブジェクト指向プログラミング言語「Ruby」の文法を一から学ぶための入門連載。最新版の2.1に対応しています。連載最終回の今回は、小規模なgemの作成にチャレンジしてみましょう。gem作成の一連の流れを体験するために、逆ポーランド記法による計算機アプリケーションを作ってみましょう。
クラスファイルにアルゴリズムを閉じ込める
今回は小規模なgemなので、このまま「/lib/rpn_calculator.rb」中のRpnCalculatorモジュールにアルゴリズムを書いてしまってもよいです。ただし、ここでは、拡張性や美しさを考え、StackCalculatorクラスを作成することにします。
StackCalculatorクラスを作成する
gem内でクラスを作成する場合、名前空間の汚染を避けるためにgemのモジュール(ここではRpnCalculatorモジュール)内にクラスを作ります。今回の場合だと、「RpnCalculator::StackCalculator」となります。
また、クラスを記述したソースコードを置く場所も、名前空間に対応するようにしましょう。今回作成するクラスは「RpnCalculator::StackCalculator」なので、「/lib/rpn_calculator/stack_calculator.rb」にクラスの内容を書いていきます。
アルゴリズムのコーディング
では、StackCalculatorクラスのコードを以下に示します。まずはザッと眺めてみて、どのようなコードになっているかを考えてみてください。
module RpnCalculator class StackCalculator def initialize(formula) @formula = formula @stack = [] end def calc @formula.each do |e| if numeric?(e) @stack.push(e) else op1 = @stack.pop.to_f op2 = @stack.pop.to_f result = op2.send(e, op1) @stack.push(result) end end @stack.first end private def numeric?(s) begin Float(s) true rescue ArgumentError false end end end end
1行目でRpnCalculatorモジュールの宣言が、2行目でStackCalculatorクラスの宣言が始まっています。このように入れ子になっているのは、StackCalculatorクラスをRpnCalculatorモジュールに閉じ込めるためです。
では、各メソッドの役割について見ていきましょう。
- initialize
「StackCalculator.new」を実行したときに呼ばれるコンストラクター。ここでクラス変数「@formula」「@stack」を初期化する - numeric?
引数の値が実数(Float)に変換できるかをチェックするためのメソッド。例外によるちょっとしたトリックを使っている(後述) - calc
「@formula」に格納された数式を1つずつ取り出し、スタック「@stack」を使って計算するメソッド。アルゴリズムのメインとなる
さらに詳細に解説します。
- numeric?メソッド
数式から取り出してきた値が文字列などの非数値オブジェクトかもしれないので、数値に変換できるかをチェックする必要があります。
「Float(s)」は「s.to_f」のように、変数「s」を実数に変換した値を返します。ただし、「s.to_f」だと実数として無効な文字列の場合0.0が返ってくるのに対して、「Float(s)」は無効な文字列だとArgumentErrorが発生します。
このように、numeric?メソッドでは、「Float(s)」の挙動を利用して「s」が実数に変換できるかをチェックします。
- calcメソッド
アルゴリズムの本体です。
9行目で「@formula」から要素を1つずつ取り出し、10行目で実数に変換できるかをチェックします。もし変換できるならば、被演算子だとみなして、11行目でスタックに要素を積みます。もし変換できなければ、演算子だとみなして13行目以降の演算を行います。
13行目と14行目でスタックの上から値を2つ取り出し、実数に変換してから15行目で演算を行います。
15行目では、ちょっとしたメタプログラミングのトリックを使っています。「send」メソッドは、レシーバーのオブジェクトのメソッドを呼ぶためのメソッドです。例えば、以下の3行のコードは等価です。
alice.say("hi") alice.send(:say, "hi") alice.send("say", "hi")
演算子もメソッドであることを思い出してください! もし「e」に「-」という文字列が格納されている場合、15行目では以下のようなコードが実行されることになります。
result = op2.send("-", op1)
「-」メソッドは引数を1つとり、自分自身と引数の値を減算したものを返すので、結果的に「op2 - op1」という演算が行われ、変数「result」に格納されます。
もし、sendを使ったメタプログラミングのトリックを使わない場合、対応させたい演算子の種類の数だけ、以下のようなコードを延々と書くことになってしまいます。
case e when "+" op2 + op1 when "-" op2 - op1 when "*" op2 * op1 : : end
これは明らかに無駄ですよね? このように、メタプログラミングを駆使すれば、少ない行でたくさんのことができるようになるのです!
17行目で「result」変数に格納された演算結果をスタックに積み、eachループの先頭に戻ります。
全ての数式の要素を処理したら、21行目でスタックの先頭の内容を返してメソッドは終了します。
RpnCalculatorモジュールから呼び出す
せっかく作ったStackCalculatorも、RpnCalculatorから呼び出してあげないと役に立ちません。「/lib/rpn_calculator.rb」に変更を加えましょう。
require "rpn_calculator/version" require "rpn_calculator/stack_calculator" module RpnCalculator class << self def run(args) calculator = StackCalculator.new(args) puts calculator.calc end end end
2行目の「require "rpn_calculator/stack_calculator"」を書き忘れると、stack_calculator.rbが読み込まれなくなるので、エラーで止まってしまいます。
ここでは、7行目でStackCalculatorオブジェクトを生成し、変数「calculator」に格納しています。8行目でcalculatorのcalcメソッドを呼び出すことで計算させ、その出力をターミナルに出力しています。
計算機プログラムの動作確認
では、動作確認をしてみましょう! プロジェクトのルートディレクトリで、以下のコマンドを実行してみてください。
$ bundle exec bin/rpn_calculator 10.5 2 7 + -
ここまでの作業で間違いがなければ、以下のような出力が得られるでしょう。
$ bundle exec bin/rpn_calculator 10.5 2 7 + - 1.5
きちんと計算できていますね!
Copyright © ITmedia, Inc. All Rights Reserved.
関連記事
- 数々の“スペル”で高度なプログラミング:Rubyの魔術
- 公用語に英語、「再起動」したRubyKaigi 2013が東京で開催
いったん終了していたRubyコミュニティ主催の年次イベントが再開。技術色、国際色を強め、盛況のうちに幕を閉じた - Web業界、今から行くならRubyエンジニアが狙い目?
不況で冷え込んでいたIT業界の転職市場に、回復の兆しが見え始めている。だが、業種や職種によって採用数や条件に大きな差異が生まれている。転職市場の動向を追い、自身のキャリア戦略立案に生かしてほしい。 - Ruby 2.0.0がリリース、大規模化対応の機能などを搭載
生誕20年となる節目を迎えて、プログラミング言語「Ruby」の最新版がリリースされた - 新バージョンで何が変わるのか、Rubyはどこへ向かうのか:まつもと×笹田、Ruby 1.9を語る
- いよいよ始まるRuby 1.9への移行:開発コアメンバが語るRubyの今とこれから(前編)
- Rubyの今後の進化の方向性とは?:開発コアメンバが語るRubyの今とこれから(後編)
- 互換性や脆弱性の問題にどう対応していくのか:Rubyが抱える課題、NaClの前田氏が講演
- Rails Hub情報局:「なんでRubyなんか作った!? 迷惑だ!」に対するMatzの答え
- Rails Hub情報局:Rubyはイノベーション言語として選ばれている
- Rails Hub情報局:Rubyのまつもと氏は、一発屋で終わるのか?
- Rails Hub情報局:Rubyに魔法は要らない
- 数々の“スペル”で高度なプログラミング:Rubyの魔術
- 晴読雨読@エンジニアライフ:『たのしい開発 スタートアップRuby』――なぜRubyistたちはあれほど楽しそうなのか
- 「JRuby 1.7.0」登場、1年半ぶりのメジャーアップデート
JavaVM上のRuby実装「JRuby」の最新版となる「JRuby 1.7.0」が、10月22日にリリースされた。