Message from discussion
Postfix to Infix (#148)
Path: g2news1.google.com!news3.google.com!feeder1-2.proxad.net!proxad.net!feeder1-1.proxad.net!club-internet.fr!feedme-small.clubint.net!news.astraweb.com!border2.a.newsrouter.astraweb.com!feed.xsnews.nl!border-1.ams.xsnews.nl!217.145.39.3.MISMATCH!talisker.lacave.net!lacave.net!not-for-mail
From: Christian von Kleist <cvonkle...@gmail.com>
Newsgroups: comp.lang.ruby
Subject: Re: [QUIZ] Postfix to Infix (#148)
Date: Mon, 3 Dec 2007 15:56:27 -0500
Organization: Service de news de lacave.net
Lines: 294
Message-ID: <ac922e90712031256k51c57881ka5ba23374b26f143@mail.gmail.com>
References: <20071130132821.ZZSN2903.eastrmmtao105.cox.net@eastrmimpo01.cox.net> <beb1c8780712031151q5ab7776g1a10a3b712cfd107@mail.gmail.com> <39a0164e0712031244o24955d69ua17db7c54b5a74d5@mail.gmail.com>
NNTP-Posting-Host: bristol.highgroove.com
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
X-Trace: talisker.lacave.net 1196715467 47750 65.111.164.187 (3 Dec 2007 20:57:47 GMT)
X-Complaints-To: abuse@lacave.net
NNTP-Posting-Date: Mon, 3 Dec 2007 20:57:47 +0000 (UTC)
In-Reply-To: <39a0164e0712031244o24955d69ua17db7c54b5a74d5@mail.gmail.com>
X-Received-From: This message has been automatically forwarded from the ruby-talk mailing list by a gateway at comp.lang.ruby. If it is SPAM, it did not originate at comp.lang.ruby. Please report the original sender, and not us. Thanks! For more details about this gateway, please visit: http://blog.grayproductions.net/categories/the_gateway
X-Mail-Count: 281899
X-Ml-Name: ruby-talk
X-Rubymirror: Yes
X-Ruby-Talk: <ac922e90712031256k51c57881ka5ba23374b26f...@mail.gmail.com>
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