もう少しエラー処理を頑張ってみる

第 30 回 Ruby/Rails 勉強会の演習問題の RPN 電卓。
他の人の解答を見ると、エラー処理を頑張ってる人がいたので、自分も頑張ってみる。

今回の演習では、まずスタックを作って、そのスタックを使って RPN を作る。
空のスタックから pop しようとすると Stack::EmptyStackError 例外が出る仕様。

なので、前回の RPN 電卓 その弍 に例外ハンドラを書いてみる。

require 'stack'

def rpn(exp)
  num = Stack.new
  exp.each do |i|
    if /\D/ =~ i
      num.push(num.pop.__send__(i, num.pop))
    else
      num.push i.to_i
    end
  end
  raise SyntaxError  if num.size > 1  # 数が残ってるがな
  num.pop
end

if $0 == __FILE__
  begin
    puts "#{ARGV.join(' ')} = #{rpn(ARGV)}"
  rescue ZeroDivisionError
    STDERR.puts "zero divide: #{ARGV.join(' ')}"
  rescue NoMethodError, ArgumentError
    STDERR.puts "invalid operator: #{ARGV.join(' ')}"
  rescue Stack::EmptyStackError
    STDERR.puts "too many operator: #{ARGV.join(' ')}"
  rescue SyntaxError
    STDERR.puts "too few operator: #{ARGV.join(' ')}"
  end
end

例外を使うと、エラー処理を 1 ヶ所にまとめられる (ことが多い) のでコードがスッキリする。

$ ./rpn.rb 4 3 2 \* /
4 3 2 * / = 1

$ ./rpn.rb 4 7 %       # +-*/ 以外も使えてお得
4 7 % = 3

$ ./rpn.rb 4 7 to_f /  # 二項演算子でないと呼べない
invalid operator: 4 7 to_f /

$ ./rpn.rb 4 7 @       # @ ってどんな演算だよ
invalid operator: 4 7 @

$ ./rpn.rb 0 3 /       # 0 で割れない
zero divide: 0 3 /

$ ./rpn.rb 4 7 % +     # 演算子が多い
too many operator: 4 7 % +

$ ./rpn.rb 4 7 % 8     # 数値が多い
too few operator: 4 7 % 8