ちなみに

火曜日の空は僕を押しつぶした。

後置記法での計算の添削に便乗してみた

UKさんの404 NotFound|ステキなウエディングをうじひささんがnowa サービス終了のお知らせにて添削されていたので、便乗して僕も書いてみた。まだうじひささんのコードは読んでない。いま僕が持ってる知識(当然マニュアルは参照した)を使って僕ならどう書くかという現段階の僕の答えです。

まず、元のコード

class String
  def rpn
    @expr = self.split(" ")
    stack = Array.new
 
    @expr.each {|i|
      if i == "+" || i == "-" || i == "*" || i == "/"
        stack = calc(i, stack)
      else
        stack << i
      end
    }
 
    return stack[0]
  end
 
  private
  def calc(operator, stack)
    return Array(stack[1..-1].inject(stack[0].to_i){|result, i|
      reslut = result.__send__(operator, i.to_i)
    })
  end
end

テスト用コードは表示できるようにした以下のものを使います

require 'rpn.rb'
p "2 8 +".rpn
p "4 6 -".rpn
p "81 9 /".rpn
p "4 3 *".rpn
p "3 7 + 10 20 + 2 * 10 + 80 -".rpn

そして、おいらのコード

class String
  # Reverse Polish Notation
  # '4 5 +'.rpn => 4 + 5 = 9
  def rpn
    @stack = []

    self.split(' ').each do |i|
      case i
      when /\d/
        @stack.push(i.to_i)
      when %r|[-+*/]|
        @stack.push(i.calc(@stack.pop, @stack.pop)) if @stack.size >= 2
      end
    end
    @stack.first
  end

  protected

  # Calculation Function
  # '+'.calc(4, 5) => 9
  def calc(n, p)
    case self
    when '+'
      p + n
    when '-'
      p - n
    when '*'
      p * n
    when '/'
      p / n
    end
  end
end

結果

% ruby rpn_test.rb
10
-2
9
12
10

おっけー。でも最後のは式自体が間違ってる(後述)

意識した点

  • 文字列は式展開する時以外はシングルクォート
  • 配列の宣言はなんらかの理由で意識的にする場合以外は[]
  • 式の配列は別に作らずに、splitの戻り値をeachで直接回す
  • 複数行に渡るブロックはdo-endではさむ
  • if-elseじゃなくてcase-whenでまとめる
  • 演算子をまとめたり、数字のみで指定したかったのでwhenの条件式を正規表現
  • スタックを意識する為にpush&popメソッド
  • 配列の一番目へのアクセスはfirstメソッド
  • returnは使わない
  • calc内がややこしかったので単純に演算子を判別して計算するだけ(もっとスマートに書きたい><)
  • 現在スタックされてる数はチェックしてるが、他の例外処理は今回は省略
    • 演算子と数値の数の不一致とか、数字じゃなかったらどうするかとか、0の割り算とか・・・
  • 最後の式は数の不一致で最初の3+7の答えが結果になってしまってる><
    • もしかしてわざとなのでしょうか?

結論

もっと勉強してきます><
がんば、僕。

とりあえず今からうじひささんの回答を見る。しかし、正解というのはないと思うのであくまで参考ということで。

余談

うじひささんのことが大好きすぎて困ってます。研究室を特定して、院入試の概要見たりなんかしてませんよ。

追記

読んだ!土俵が違った!もっとがんばる><

テストコード書けるようにならなきゃだ。

さらに追記

あれ、逆ポーランド記法って2つ以上の組み合わせにも対応してるんですか?それなら計算合いますね。(というかinjectにしてるのがその処理ですね。)
むーほんとに土俵が違ったようだ。
出直そう。

今日の成果

class String
  # Reverse Polish Notation
  # '4 5 +'.rpn => 4 + 5 = 9
  def rpn
    stack = []
    opr = %q[ + - * / ]

    split(' ').each do |i|
      if opr.include? i
        stack[-1] = stack[-2].__send__(i, stack.pop)
      else
        stack << i.to_i
      end
    end
    stack.first
  end
end

いろいろパクって、いろいろ自分のスタイルを曲げた><
まだスタイルが確定してないからいいの!
あと、逆ポーランド記法は間違ってないと仮定して、例外処理を一切排除。

注:調べてるけど、多分逆ポーランド記法は2つの数字の組に対して演算していくはず。