Account Options

  1. Sign in
The old Google Groups will be going away soon, but your browser is incompatible with the new version.
Google Groups Home
« Groups Home
Message from discussion Postfix to Infix (#148)
The group you are posting to is a Usenet group. Messages posted to this group will make your email address visible to anyone on the Internet.
Your reply message has not been sent.
Your post was successful
 
From:
To:
Cc:
Followup To:
Add Cc | Add Followup-to | Edit Subject
Subject:
Validation:
For verification purposes please type the characters you see in the picture below or the numbers you hear by clicking the accessibility icon. Listen and type the numbers you hear
 
Christian von Kleist  
View profile  
 More options Dec 3 2007, 3:56 pm
Newsgroups: comp.lang.ruby
From: Christian von Kleist <cvonkle...@gmail.com>
Date: Mon, 3 Dec 2007 15:56:27 -0500
Local: Mon, Dec 3 2007 3:56 pm
Subject: Re: [QUIZ] Postfix to Infix (#148)
On Dec 3, 2007 3:44 PM, Eric DUMINIL <eric.dumi...@gmail.com> wrote:

> Since Pastie doesn't seem to be so reliable (at least today):

> ###########################################
> # Ruby quiz #148
> # http://www.rubyquiz.com/quiz148.html
> # Eric Duminil
> # 02/12/2007
> #
> ### It removes unnecesary ().
> #
> #  ruby postfix_to_infix.rb '56 34 213.7 + * 678 -'
> #  56*(34+213.7)-678
> #  =13193.2
> #
> #  ruby postfix_to_infix.rb '1 56 35 + 16 9 - / +'
> #  1+(56+35)/(16-9)
> #  =14
> #
> #  ruby postfix_to_infix.rb '1 2+ 4* 5*'
> #  (1+2)*4*5
> #  =60
> #
> ### You can omit spaces between operands and operators
> #
> #  ruby postfix_to_infix.rb '1 2+ 4* 5+ 3-'
> #  (1+2)*4+5-3
> #  =14
> #
> #  ruby postfix_to_infix.rb '1 2+ 3 4 + +'
> #  1+2+3+4
> #  =10
> #
> #  ruby postfix_to_infix.rb '1 2 - 3 4 - -'
> #  1-2-(3-4)
> #  =0
> #
> ### You can use either ** or ^
> ### which actually raises troubles while parsing : is "**" == "* *" or
> "**"=="^"  ?
> #
> #  ruby postfix_to_infix.rb '2 2 ^ 2 ^'
> #  (2^2)^2
> #  =16
> #
> #  ruby postfix_to_infix.rb '2 2 ** 2 **'
> #  (2**2)**2
> #  =16
> #
> #  ruby postfix_to_infix.rb '2 3 4 ** **'
> #  2**3**4
> #  =2417851639229258349412352
> #
> #  ruby postfix_to_infix.rb '2 3 ** 4 **'
> #  (2**3)**4
> #  =4096
> #
> ### It raises when something's missing
> #
> #  ruby postfix_to_infix.rb '1 2+ 4* 5+ 3'
> #  postfix_to_infix.rb:94:in `convert_to_infix': ArgumentError
> #
> #
> ### No UnaryOperation yet

> class Operation
>   attr_reader :operator, :operands
>   attr_writer :with_parentheses
>   def initialize(operator, *operands)
>     @operator=operator
>     @operands=operands
>     @with_parentheses=false
>     add_parentheses_to_operands_if_necessary!
>   end

>   def has_parentheses?
>     @with_parentheses
>   end
> end

> class BinaryOperation<Operation
>   @@precedence_over={
>     '+' =>['',''],
>     #no need to put () before -
>     '-'  =>['','- +'],
>     '*' => ['- +','- +'],
>     '/' => ['- +','- + * /'],
>     '**'=>['- + * / ^ **','- + * /'],
>     '^'=>['- + * / ^ **','- + * /']
>   }

>   def to_s
>     operands.collect{|operand| if operand.is_a?(Operation) &&
> operand.has_parentheses? then
>         "(#{operand})"
>       else
>         operand
>       end
>     }.join(operator)
>   end

>   private

>   def add_parentheses_to_operands_if_necessary!
>     operands.each_with_index{|operand,i|
>       operators_with_lower_priority=@@precedence_over[operator][i].split(' ')
>       operand.with_parentheses=operators_with_lower_priority.any?{|o|
> operand.operator == o} if operand.is_a?(BinaryOperation)
>     }
>   end
> end

> class Postfix<String
>   def split_into_operands_and_operators
>     self.scan(/([\w\.]+|\*+|\+|\/|\-|\^)/).flatten
>   end

>   def to_infix
>     @to_infix||=convert_to_infix
>   end

>   def evaluate
>     eval(self.to_infix.gsub(/\^/,'**'))
>   end

>   private

>   def convert_to_infix
>     stack=[]
>     self.split_into_operands_and_operators.each{|term|
>       if term=~/^[\w\.]+$/ then
>         stack<<term
>       else
>         right_operand,left_operand=stack.pop,stack.pop
>         stack<<BinaryOperation.new(term,left_operand,right_operand)
>       end
>     }
>     raise ArgumentError unless stack.size==1
>     stack.first.to_s
>   end
> end

> p=Postfix.new(ARGV[0])
> puts p.to_infix
> puts "=#{p.evaluate}"

> ###########################################

> see http://pastie.caboo.se/124473 for colored syntaxing

> Thanks again,

> Eric

> On 03/12/2007, Artem Voroztsov <artem.vorozt...@gmail.com> wrote:
> > All solutions you posted are fun. But now it's time for REAL challenge. :)))

> >   'a b c d = 1 2 + and = ='
> >     should be converted to
> >   'a = b = ( c = d and 1 + 2 )'

> > My program does this.

> > The final task is: having information about operators priorities,
> > commutativity/non-commutativity, and associativity type (left or
> > right)
> > construct OP_STRENGTH

> > input = 'a b c d = 1 2 + and = ='

> >   #
> >   OP_STRENGTH = {
> >     :left  => {'and'=>-1, '='=>1, '+'=>2, '-'=>2, '*'=>4, '/'=>4},
> >     :right => {'and'=>-1, '='=>0 ,'+'=>2, '-'=>3, '*'=>4, '/'=>5}
> >   }

> >   def parenthesize(triplet, top_op_strength, side)
> >     q = [ [triplet, top_op_strength, side] ]
> >     while !q.empty?
> >       t,top_op_strength,side = q.pop
> >       if t.is_a?(Array)
> >         if OP_STRENGTH[side][t[1]] < top_op_strength
> >           print '( '
> >           q << ')'
> >         end
> >         q << [t[2], OP_STRENGTH[:right][t[1]], :left]
> >         q << t[1]
> >         q << [t[0], OP_STRENGTH[:left][t[1]], :right]
> >       else
> >         print t, ' '
> >       end
> >     end
> >   end

> >   require 'benchmark'
> >   puts Benchmark.measure {
> >     stack = []
> >     input.strip.split.each do |token|
> >       case token
> >       when '*', '+', '/', '-', '=', 'and'
> >         stack << [stack.pop, token, stack.pop].reverse!
> >       else
> >         stack << token
> >       end
> >     end

> >     parenthesize(stack.last, 0, :right)
> >     puts
> >   }

> > And the second thing.
> > For inputs

> >   '0 ' + (1..10000).to_a.join(' - ') + ' *'
> >   (1..N).to_a.join(' ') + ' /' * (N-1)

> > where N = 10000 i have benchmark:

> >   0.282000   0.000000   0.282000
> >   0.313000   0.000000   0.313000

OOO = "*/+-"
COM = "+*"

def postfix_to_infix(expression)
  terms = []
  expression.split(/\s/).each do |p|
    terms << p
    if OOO.include?(p)
      terms << [terms.slice!(-1), terms.slice!(-2), terms.slice!(-1)]
    end
  end
  peval(terms[0])
end

def peval(terms, parent_o = nil, is_right = false)
  return [terms, terms.to_f] unless terms.class == Array

  o = terms[0]
  a, a_val = peval(terms[1], o)
  b, b_val = peval(terms[2], o, true)

  sval = [a, o, b].join(' ')
  nval = a_val.send(o, b_val)

  if (OOO.index(o) > OOO.index(parent_o || o)) ||
    (!COM.include?(o) && OOO.index(o) == OOO.index(parent_o || o) && is_right)
    sval = '(' + sval + ')'
  end

  [sval, nval]
end

res = postfix_to_infix(ARGV[0])
puts res[0], res[1]

% ruby /tmp/rt.rb '56 34 213.7 + * 678 -'
56 * (34 + 213.7) - 678
13193.2

Some tests:

56 * (34 + 213.7) - 678 == 56 * (34 + 213.7) - 678 -> pass
2 + 3 == 2 + 3 -> pass
1 + (56 + 35) / (16 - 9) == 1 + (56 + 35) / (16 - 9) -> pass
1 + 2 + 3 + 4 == 1 + 2 + 3 + 4 -> pass
1 - 2 - (3 - 4) == 1 - 2 - (3 - 4) -> pass
5 - (1 - 2 - (3 - 4)) == 5 - (1 - 2 - (3 - 4)) -> pass
5 / (1 / 2 / (3 / 4)) == 5 / (1 / 2 / (3 / 4)) -> pass
2 / 7 / 9 == 2 / 7 / 9 -> pass
2 / (7 / 9) == 2 / (7 / 9) -> pass


 
You must Sign in before you can post messages.
To post a message you must first join this group.
Please update your nickname on the subscription settings page before posting.
You do not have the permission required to post.