式の計算(カッコあり、演算子の結合優先順位なし)
何も見ずに書いたのですが、逆ポーランド記法に変換する処理に頭を使いました><
まだ演算子の結合優先順位が残ってる、ひー><
# 式を逆ポーランド記法に変換して計算 # カッコあり # 演算子の結合優先順位なし token_defs = [ [:space, /\s+/], [:operator, /[+\-*\/]/], [:numeric, /-?[0-9]+(?:\.[0-9]+)?/], [:leftkakko, /\(/], [:rightkakko, /\)/], ] input = gets.chomp tokens = [] i = 0 while input[i] find = nil token_defs.each do |token_def| find = ( /\A#{token_def[1]}/ =~ input[i..-1] ) if find tokens << [token_def[0], $&] unless token_def[0] == :space i += find + $&.size break end end raise unless find end # 逆ポーランド記法に変換する # 右辺の括弧レベルを記録する # (右辺の終了を調べるため) rs_stack = [] kakko_level = 0 src_tokens = tokens dest_tokens = [] # 逆ポーランド記法のトークン dest_tokens_pointer = 0 before_src_token_type = :none src_tokens.each do |src_token| case src_token[0] when :numeric raise unless [:none, :leftkakko, :operator].include?( before_src_token_type ) dest_tokens.insert( dest_tokens_pointer, src_token ) dest_tokens_pointer += 1 if before_src_token_type == :operator # 右辺の場合 if rs_stack.last == kakko_level rs_stack.pop dest_tokens_pointer += 1 end end when :operator raise unless [:numeric, :rightkakko].include?( before_src_token_type ) dest_tokens.insert( dest_tokens_pointer, src_token ) rs_stack.push( kakko_level ) when :leftkakko raise unless [:none, :operator, :leftkakko].include?( before_src_token_type ) kakko_level += 1 when :rightkakko raise unless kakko_level > 0 raise unless [:numeric, :rightkakko].include?( before_src_token_type ) kakko_level -= 1 if rs_stack.last == kakko_level rs_stack.pop dest_tokens_pointer += 1 end else raise end before_src_token_type = src_token[0] end raise unless dest_tokens.size == dest_tokens_pointer raise unless kakko_level == 0 # 逆ポーランド記法の式を表示 puts dest_tokens.map{|v|v[1]}.join(' ') # 逆ポーランド記法の式を計算する tokens = dest_tokens stack = [] tokens.each do |token| case token[0] when :numeric stack.push token[1].to_f when :operator raise unless stack.size >= 2 b = stack.pop a = stack.pop case token[1] when '+' a += b when '-' a -= b when '*' a *= b when '/' a /= b else raise end stack.push a end end raise unless stack.size == 1 puts stack[0]