Skip to content

Instantly share code, notes, and snippets.

@numbata
Created July 18, 2012 22:50
Show Gist options
  • Save numbata/3139500 to your computer and use it in GitHub Desktop.
Save numbata/3139500 to your computer and use it in GitHub Desktop.

Revisions

  1. numbata created this gist Jul 18, 2012.
    5 changes: 5 additions & 0 deletions gistfile1.sh
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,5 @@
    # Example

    > ./rpn.rb '3 + 4 * 2 / (1 - 5) * 2'
    RPN: 3 4 2 * 1 5 - / 2 * +
    Result: -1.0
    87 changes: 87 additions & 0 deletions rpn.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,87 @@
    #!/usr/bin/env ruby

    class RPN
    class Token < String
    OPS_PRIORITY_MAP = [
    '',
    %w(- +),
    %w(* /)
    ]

    def priority
    OPS_PRIORITY_MAP.index{|ops| ops.include? self} || 0
    end

    def operator?
    OPS_PRIORITY_MAP.flatten.include? self
    end

    def open_bracket?
    self == '('
    end


    def close_bracket?
    self == ')'
    end
    end

    def self.transform(expression)
    result = []
    operators = []

    self.operands(expression).each do |token|
    if token.operator?
    if operators.last && token.priority <= operators.last.priority
    result << operators.pop
    end
    operators << token
    elsif token.close_bracket?
    until operators.last.open_bracket? do
    result << operators.pop
    end
    operators.pop
    elsif token.open_bracket?
    operators << token
    else
    result << token
    end
    end

    (result + operators.reverse).join(' ')
    end

    def self.calc(expression)
    stack = []
    operands(expression).each do |token|
    if token.operator?
    b = stack.pop.to_f
    a = stack.pop.to_f
    stack << a.send(token.to_s, b)
    else
    stack << token
    end
    end

    stack.last
    end

    private

    # Split string to operators
    #
    # @param String expression
    # @return Array
    def self.operands expression
    ops = Token::OPS_PRIORITY_MAP.flatten
    expression.split(/(?:\s+|(?=[0-9]+)(?<![0-9])|(?=[#{ops.join}()]))/).map{|x| Token.new(x)}
    end

    end


    expression = ARGV.join(' ')
    rpn_expression = RPN.transform(expression)
    puts "RPN: #{rpn_expression}"
    result = RPN.calc(rpn_expression)
    puts "Result: #{result}"