[QUIZ] Postfix to Infix (#148)

147 views
Skip to first unread message

Ruby Quiz

unread,
Nov 30, 2007, 8:28:17 AM11/30/07
to
The three rules of Ruby Quiz:

1. Please do not post any solutions or spoiler discussion for this quiz until
48 hours have passed from the time on this message.

2. Support Ruby Quiz by submitting ideas as often as you can:

http://www.rubyquiz.com/

3. Enjoy!

Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone
on Ruby Talk follow the discussion. Please reply to the original quiz message,
if you can.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

There are many different ways to write mathematical equations. Infix notation
is probably the most popular and yields expressions like:

2 * (3 + 5)

Some people like to work with a postfix notation (often called Reverse Polish
Notation or just RPN) though, which doesn't require parentheses for the same
equation:

2 3 5 + *

You can compare the results of these equations using the Unix utilities bc
(infix) and dc (postfix):

$ bc <<< '2 * (3 + 5)'
16
$ dc <<< '2 3 5 + * p'
16

The "p" instruction tacked onto the end of the expression for dc just tells it
to print the result.

This week's quiz is to write a script that translates postfix expressions into
the equivalent infix expression. In the simplest form, your script should
function as such:

$ ruby postfix_to_infix.rb '2 3 +'
2 + 3

At minimum, try to support the four basic math operators: +, -, *, and /. Feel
free to add others though. For numbers, remember to accept decimal values.

You can count on the postfix expressions having spaces between each term, if you
like. While dc is content with 2 3+p, you don't have to support it unless you
want to.

For an added bonus, try to keep the parentheses added to infix expressions to
the minimum of what is needed. For example, prefer these results:

$ ruby postfix_to_infix.rb '56 34 213.7 + * 678 -'
56 * (34 + 213.7) - 678
$ ruby postfix_to_infix.rb '1 56 35 + 16 9 - / +'
1 + (56 + 35) / (16 - 9)

to these:

$ ruby postfix_to_infix.rb '56 34 213.7 + * 678 -'
((56 * (34 + 213.7)) - 678)
$ ruby postfix_to_infix.rb '1 56 35 + 16 9 - / +'
(1 + ((56 + 35) / (16 - 9)))

Posting equations and your output is not a spoiler.

Christian von Kleist

unread,
Nov 30, 2007, 10:24:20 AM11/30/07
to

I had this as an assignment for a class once, in Java, so I'll sit
this one out. Fun quiz!

Robert Dober

unread,
Nov 30, 2007, 2:51:05 PM11/30/07
to
Why? I'd rather say that it is quite challange to provide a solution
in Ruby that is not just a rewrite of your Java solution.
I do not think that there are an "ethic" considerations ;).
Cheers
Robert

--

http://ruby-smalltalk.blogspot.com/

---
All truth passes through three stages. First, it is ridiculed. Second,
it is violently opposed. Third, it is accepted as being self-evident.
Schopenhauer (attr.)

Daniel Martin

unread,
Dec 1, 2007, 5:18:37 PM12/1/07
to
"Christian von Kleist" <cvonk...@gmail.com> writes:

> I had this as an assignment for a class once, in Java, so I'll sit
> this one out. Fun quiz!

Well, doubtless in java you solved it in a way that demonstrated your
use of certain basic data structures that you'd just learned about.

There are at least two completely different algorithms to use in
solving this in a conventional manner, and I've just solved it in a
third, completely unconventional ("evil") manner. So solve it in a
manner different from what you did in class.

(The no-spoiler rule makes phrasing this reply difficult)

Also, being strict about the minimal parentheses rule makes things a
bit interesting:

'1 2 + 3 4 + +' should become '1 + 2 + 3 + 4'

But:

'1 2 - 3 4 - -' should become '1 - 2 - (3 - 4)'

Likewise for multiplication and division.

Also, you could add exponentiation, but if you do remember that infix
exponentiation associates to the right, not the left, so:

'2 2 ^ 2 ^' should become '(2 ^ 2) ^ 2'

But:

'2 2 2 ^ ^' should become '2 ^ 2 ^ 2'

So really, there's lots more to it than whatever you did it in that
java class.

--
s=%q( Daniel Martin -- mar...@snowplow.org
puts "s=%q(#{s})",s.to_a.last )
puts "s=%q(#{s})",s.to_a.last

Todd Benson

unread,
Dec 1, 2007, 5:27:47 PM12/1/07
to
On Dec 1, 2007 4:18 PM, Daniel Martin <mar...@snowplow.org> wrote:
> "Christian von Kleist" <cvonk...@gmail.com> writes:
> But:
>
> '1 2 - 3 4 - -' should become '1 - 2 - (3 - 4)'

Or '1 - 2 - 3 + 4' (yikes!) :^0

Todd

Eric I.

unread,
Dec 1, 2007, 6:02:01 PM12/1/07
to
On Dec 1, 5:27 pm, Todd Benson <caduce...@gmail.com> wrote:
> On Dec 1, 2007 4:18 PM, Daniel Martin <mar...@snowplow.org> wrote:
>
> > "Christian von Kleist" <cvonkle...@gmail.com> writes:
> > But:
>
> > '1 2 - 3 4 - -' should become '1 - 2 - (3 - 4)'
>
> Or '1 - 2 - 3 + 4' (yikes!) :^0

Well one feature of the Ruby Quiz is that our Quiz Master generally
allows submitters quite a bit of flexibility in reinterpreting the
problem. To me, however, that form seems outside the problem
description, as you're a) applying the distributive property, and b)
ending up with a different set of operators than what you started with
(from 3 minuses to 2 minuses and 1 plus).

And once you start down the road '4 2 3 + *' could become '4 * (2 +
3)', '8 + 12', or even '20'.

Eric

====

Are you interested in on-site Ruby training that's been highly
reviewed by former students? http://LearnRuby.com

Todd Benson

unread,
Dec 1, 2007, 6:30:58 PM12/1/07
to
On Dec 1, 2007 5:05 PM, Eric I. <rubytr...@gmail.com> wrote:
> On Dec 1, 5:27 pm, Todd Benson <caduce...@gmail.com> wrote:
> > On Dec 1, 2007 4:18 PM, Daniel Martin <mar...@snowplow.org> wrote:
> >
> > > "Christian von Kleist" <cvonkle...@gmail.com> writes:
> > > But:
> >
> > > '1 2 - 3 4 - -' should become '1 - 2 - (3 - 4)'
> >
> > Or '1 - 2 - 3 + 4' (yikes!) :^0
>
> Well one feature of the Ruby Quiz is that our Quiz Master generally
> allows submitters quite a bit of flexibility in reinterpreting the
> problem. To me, however, that form seems outside the problem
> description, as you're a) applying the distributive property, and b)
> ending up with a different set of operators than what you started with
> (from 3 minuses to 2 minuses and 1 plus).
>
> And once you start down the road '4 2 3 + *' could become '4 * (2 +
> 3)', '8 + 12', or even '20'.
>
> Eric

Of course. It's not a quiz about simplification. I just thought it
was funny (a stretch on the minimizing of parentheses). It keeps the
same digits, though; only changes the operators. It's quite clear
that the only symbols -- digits or operators -- we're allowed to add
or remove are ) and (.

Cheers,
Todd

Robert Dober

unread,
Dec 2, 2007, 8:52:50 AM12/2/07
to
Here goes my solution for this Quiz, I am confident that solutions 1
and 2 are correct, well 3 too, for 4 that is less sure ;).
Nice to have a quiz which can be solved in less time again.

Robert
###############################
# Read postfix from args or stdin
# Print an infix solution *without* paranthesis
postfix = ARGV.empty? ? $stdin.read.split : ARGV
postfix = postfix.map{ | ele | Integer( ele ) rescue ele }
stack = []
postfix.each do | ele |
case ele
when Integer
stack << ele
else
rhs = stack.pop
stack[ -1 ] = stack[ -1 ].send( ele, rhs )
end
end
puts stack.first.to_s
#########################################################"
# Read postfix from args or stdin
# Print an infix solution with *many* paranthesis

postfix = ARGV.empty? ? $stdin.read.split : ARGV
postfix = postfix.map{ | ele | Integer( ele ) rescue ele }
stack = []
postfix.each do | ele |
case ele
when Integer
stack << ele
else
rhs = stack.pop
stack[ -1 ] = "( #{stack[ -1 ]} ) #{ele} ( #{rhs} )"
end
end

puts stack.first
##################################################
# Read postfix from args or stdin
# Print an infix solution with *some* paranthesis
# the stupid ( and expensive ) way.

class Expression
Combinations = [
["", "", "", ""],
["( ", " )", "", ""],
["", "", "( ", " )"],
["( ", " )", "( ", " )"]
]
attr_reader :text, :value
def initialize text
@value = Integer( text )
@text = text
end
def apply op, rhs
new_text = "#@text #{op} #{rhs.text}"
@value = @value.send( op, rhs.value )
Combinations.each do | parens |
txt = ["", @text, " #{op} ", rhs.text ].
zip( parens ).flatten.join
if eval( txt ) == @value then
return @text = txt
end
end
raise RuntimeError, "ooops"
end
end

postfix = ARGV.empty? ? $stdin.read.split : ARGV
postfix = postfix.map{ | ele | Expression.new( ele ) rescue ele }
stack = []
postfix.each do | ele |
case ele
when Expression
stack << ele
else
rhs = stack.pop
stack.last.apply ele, rhs
end
end

puts stack.first.text
#############################################
# Read postfix from args or stdin
# Print an infix solution with *some* paranthesis
# (the clever way?)

class Expression
Priorities = {
"**" => 2,
"*" => 1, "/" => 1, "%" => 1,
"+" => 0, "-" => 0,
nil => 3
}
Commutative = %w[ * + ]
attr_reader :text, :top_op
def initialize text
@top_op = nil
@text = text
end

def apply op, rhs
@text = parented_text( op ) +
" #{op} " << rhs.parented_text( op, false )
@top_op = op
end

def comm? op
Commutative.include? op
end

def parented_text op, is_lhs=true
my_prio = Priorities[ @top_op ]
op_prio = Priorities[ op ]
return @text if op_prio < my_prio
return "( #@text )" if op_prio > my_prio
return @text if comm?( op ) || is_lhs
"( #@text )"
end

end
postfix = ARGV.empty? ? $stdin.read.split : ARGV
postfix = postfix.map{ | ele |
Expression::Priorities[ ele ] ? ele : Expression.new( ele )
}
stack = []
postfix.each do | ele |
case ele
when Expression
stack << ele
else
rhs = stack.pop
stack[ -1 ].apply ele, rhs
end
end

puts stack.first.text

Eric DUMINIL

unread,
Dec 2, 2007, 9:28:32 AM12/2/07
to
Here is my solution:

http://pastie.caboo.se/124288
I'm pretty it removes every unnecessary (), at least for - + ** ^ /.


Thanks for the quiz!

Harry Kakueki

unread,
Dec 2, 2007, 10:09:23 AM12/2/07
to
>
> This week's quiz is to write a script that translates postfix expressions into
> the equivalent infix expression. In the simplest form, your script should
> function as such:
>
> $ ruby postfix_to_infix.rb '2 3 +'
> 2 + 3
>
> At minimum, try to support the four basic math operators: +, -, *, and /. Feel
> free to add others though. For numbers, remember to accept decimal values.
>
# Here is my solution.
# It solves the minimum requirements with minimal error checking.
# Removes some parentheses.
#str = "1 56 35 + 16 9 - / +"
str = "1 2 - 3 4 - - 2 * 7.4 + 3.14 * 2 / 1.2 + 3 4 - -"
ops = %w[+ - * /]
arr = str.split(/\s+/)
err = arr.select {|c| c =~ /^\d+\.?\d?/ || ops.include?(c)}
the_stack = []

if arr.length == err.length
arr.each_with_index do |x,y|
the_stack << x unless ops.include?(x)
if ops.include?(x) && the_stack.length > 1
b = the_stack.pop
a = the_stack.pop
the_stack << "(#{a} #{x} #{b})" if (x == "+" || x == "-") && (y <
(arr.length - 1))
the_stack << "#{a} #{x} #{b}" if x == "*" || x == "/" || y ==
(arr.length - 1)
end
end
puts the_stack[0]
end

# Harry


--
A Look into Japanese Ruby List in English
http://www.kakueki.com/ruby/list.html

tho_mica_l

unread,
Dec 2, 2007, 10:34:41 AM12/2/07
to
> Some people like to work with a postfix notation (often called Reverse Polish
> Notation or just RPN) though, which doesn't require parentheses for the same
> equation:

Since I recently wrote a small RPN calculator in ruby
(http://www.vim.org/scripts/script.php?script_id=2040), I found this
idea interesting.

Here is my take on this. It provides some kind of simplicistic
pattern
matcher and should be extensible. The assumption is made that the
input
consists of numeric data and predefined operators. No error checking
is
done.

Regards,
Thomas.


class Quiz148
class << self
def run(args)
iqueue = args.map {|e| e.split(/\s+/)}.flatten
return Quiz148.new(iqueue).process
end
end

def initialize(iqueue)
@iqueue = iqueue
@depth = 0
@stack = []
@ops = {
'+' => [10],
'-' => [10, [String, String, false, true]],
'*' => [5],
'/' => [5],
'^' => [5, [String, Numeric, true, false]],
}
@opnames = @ops.keys
end

def get_elt(op, idx=-1, other_value=nil)
val = @stack.delete_at(idx)
case val
when Array
eop, val = val
else
eop = nil
end
if op and eop
opp, *opatterns = @ops[op]
eopp, *epatterns = @ops[eop]
if eopp > opp
return '(%s)' % val
end
end
return val
end

def process
@iqueue.each do |token|
if @opnames.include?(token)
val1 = get_elt(token, -2)
val2 = get_elt(token, -1)
@ops[token][1..-1].each do |p1, p2, e1, e2|
if val1.kind_of?(p1) and val2.kind_of?(p2)
val1 = '(%s)' % val1 if e1
val2 = '(%s)' % val2 if e2
break
end
end
@stack << [token, '%s %s %s' % [val1, token, val2]]
else
@stack << eval(token)
end
end
# The stack should include only one element here. A check
would
# be necessary.
get_elt(nil)
end
end

if __FILE__ == $0
if ARGV.empty?
puts Quiz148.run('2 3 +') == '2 + 3'
puts Quiz148.run('56 34 213.7 + * 678 -') == '56 * (34 +
213.7) - 678'
puts Quiz148.run('1 56 35 + 16 9 - / +') == '1 + (56 + 35) /
(16 - 9)'
puts Quiz148.run('1 2 + 3 4 + +') == '1 + 2 + 3 + 4'
puts Quiz148.run('1 2 - 3 4 - -') == '1 - 2 - (3 - 4)'
puts Quiz148.run('2 2 ^ 2 ^') == '(2 ^ 2) ^ 2'
puts Quiz148.run('2 2 2 ^ ^') == '2 ^ 2 ^ 2'
else
puts Quiz148.run(ARGV)
end
end

Jesús Gabriel y Galán

unread,
Dec 2, 2007, 10:53:51 AM12/2/07
to
On Nov 30, 2007 2:28 PM, Ruby Quiz <ja...@grayproductions.net> wrote:

> This week's quiz is to write a script that translates postfix expressions into
> the equivalent infix expression. In the simplest form, your script should
> function as such:
>
> $ ruby postfix_to_infix.rb '2 3 +'
> 2 + 3
>
> At minimum, try to support the four basic math operators: +, -, *, and /. Feel
> free to add others though. For numbers, remember to accept decimal values.

> For an added bonus, try to keep the parentheses added to infix expressions to
> the minimum of what is needed. For example, prefer these results:
>
> $ ruby postfix_to_infix.rb '56 34 213.7 + * 678 -'
> 56 * (34 + 213.7) - 678
> $ ruby postfix_to_infix.rb '1 56 35 + 16 9 - / +'
> 1 + (56 + 35) / (16 - 9)
>
> to these:
>
> $ ruby postfix_to_infix.rb '56 34 213.7 + * 678 -'
> ((56 * (34 + 213.7)) - 678)
> $ ruby postfix_to_infix.rb '1 56 35 + 16 9 - / +'
> (1 + ((56 + 35) / (16 - 9)))
>

Hi,

Fortunately, this week I had some time to check the Ruby quiz, and
even to code something. Unfortunately I only had about 10 minutes, so
I only solved the basic problem:

stack = []
expr = ARGV.join(" ")
expr.split(/ /).each do |x|
case x
when *%w{+ * - /}
op2 = stack.pop
op1 = stack.pop
stack.push "(#{op1} #{x} #{op2})"
else
stack.push x
end
end
puts stack.pop

This is how it works with the examples. It doesn't remove a single
parenthesis :-(

C:\Jesus>ruby quiz148.rb 2 3 +
(2 + 3)

C:\Jesus>ruby quiz148.rb "56 34 213.7 + * 678 -"


((56 * (34 + 213.7)) - 678)

C:\Jesus>ruby quiz148.rb 1 56 35 + 16 9 - / +


(1 + ((56 + 35) / (16 - 9)))

I'll try to find some time to implement some parenthesis
simplification, although I doubt I will succeed...

Thanks,

Jesus.

Ken Bloom

unread,
Dec 2, 2007, 11:08:05 AM12/2/07
to

def convert array
cur=array.pop
case cur
when Numeric: return cur
when String:
rhs=convert(array)
lhs=convert(array)
return "(#{lhs} #{cur} #{rhs})"
end
end

equation=ARGV[0].split.collect{|x| Integer(x) rescue Float(x) rescue x}
puts convert(equation)

--
Ken (Chanoch) Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu/~kbloom1/

tho_mica_l

unread,
Dec 2, 2007, 11:22:33 AM12/2/07
to
After trinking some coffee, I added functions and templates:

class Quiz148
class << self
def run(args)
iqueue = args.map {|e| e.split(/\s+/)}.flatten
return Quiz148.new(iqueue).process
end
end

def initialize(iqueue)
@iqueue = iqueue
@depth = 0
@stack = []
@ops = {
'+' => [10],

'-' => [10, nil, [Object, String, false,


true]],
'*' => [5],
'/' => [5],

'%' => [5],
'^' => [5, nil, [String, Numeric, true,
false]],
'**' => [5, nil, [String, Numeric, true,
false]],
'sqrt' => [0, nil, [Object, true]],
'binom' => [5, '#{op}(#{val1}, #{val2})'],
}
@opnames = @ops.keys
end

def get_elt(op, idx=-1)


val = @stack.delete_at(idx)
case val
when Array
eop, val = val
else
eop = nil
end
if op and eop
opp, *opatterns = @ops[op]
eopp, *epatterns = @ops[eop]

if opp != 0 and eopp > opp


return '(%s)' % val
end
end
return val
end

def process
@iqueue.each do |token|
if @opnames.include?(token)

op = token
opp, fmt, *patterns = @ops[op]
if opp == 0
fmt ||= '#{op}#{val1}'
val1 = get_elt(token, -1)
patterns.each do |p1, e1|
if val1.kind_of?(p1)


val1 = '(%s)' % val1 if e1

break
end
end
@stack << [token, eval("\"#{fmt}\"")]
else
fmt ||= '#{val1} #{op} #{val2}'


val1 = get_elt(token, -2)
val2 = get_elt(token, -1)

patterns.each do |p1, p2, e1, e2|


if val1.kind_of?(p1) and val2.kind_of?(p2)
val1 = '(%s)' % val1 if e1
val2 = '(%s)' % val2 if e2
break
end
end

@stack << [token, eval("\"#{fmt}\"")]
end


else
@stack << eval(token)
end
end
# The stack should include only one element here. A check
would
# be necessary.
get_elt(nil)
end
end

if __FILE__ == $0
if ARGV.empty?
puts Quiz148.run('2 3 +') == '2 + 3'
puts Quiz148.run('56 34 213.7 + * 678 -') == '56 * (34 +
213.7) - 678'
puts Quiz148.run('1 56 35 + 16 9 - / +') == '1 + (56 +
35) / (16 - 9)'
puts Quiz148.run('1 2 + 3 4 + +') == '1 + 2 + 3 +
4'
puts Quiz148.run('1 2 - 3 4 - -') == '1 - 2 - (3 -
4)'

puts Quiz148.run('1 3 4 - -') == '1 - (3 - 4)'


puts Quiz148.run('2 2 ^ 2 ^') == '(2 ^ 2) ^ 2'
puts Quiz148.run('2 2 2 ^ ^') == '2 ^ 2 ^ 2'

puts Quiz148.run('2 sqrt 2 2 ^ ^') == 'sqrt(2) ^ 2
^ 2'
puts Quiz148.run('2 3 2 2 ^ ^ sqrt 3 + *') == '2 * (sqrt(3
^ 2 ^ 2) + 3)'
puts Quiz148.run('2 3 binom 2 2 ^ ^') == 'binom(2, 3)
^ 2 ^ 2'
puts Quiz148.run('1 2 3 2 2 ^ ^ binom + 3 *') == '(1 +
binom(2, 3 ^ 2 ^ 2)) * 3'
puts Quiz148.run('2 3 2 2 ^ ^ binom') == 'binom(2, 3 ^
2 ^ 2)'

James Koppel

unread,
Dec 2, 2007, 11:40:44 AM12/2/07
to
PREC = {:+ => 0,:- => 0,:* => 1,:/ => 1,:% => 1,:^ => 2}
LEFT_ASSOCS = {:+ => true,:- => true,:* => true,:/ => true,
:% => true,:^ => false}
RIGHT_ASSOCS = {:+ => true,:- => false,:* => true,:/ => false,
:% => false,:^ => true}

class TreeNode
attr_accessor :el,:left,:right
def initialize(el,left,right)
@el,@left,@right=el,left,right
end

def TreeNode.from_postfix(exp_arr)
stack = []
exp_arr.each do |exp_str|
if PREC.keys.include? exp_str.to_sym
op2,op1 = stack.pop,stack.pop
stack.push(TreeNode.new(exp_str.to_sym,op1,op2))
else
stack.push(exp_str.to_f)
end
end
stack.first
end

def to_minparen_infix
l,r = [left,right].map{|o|o.to_minparen_infix}
l = "(#{l})" if left.respond_to?(:el) and (PREC[left.el]<PREC[self.el] or
(self.el==left.el and not LEFT_ASSOCS[left.el]))
r= "(#{r})" if right.respond_to?(:el) and (PREC[right.el]<PREC[self.el] or
(self.el==right.el and not RIGHT_ASSOCS[right.el]))
l+" #{self.el} "+r
end
end

class Float
def to_minparen_infix
if self%1==0
to_i.to_s
else
to_s
end
end
end

puts TreeNode.from_postfix(ARGV.first.split(/ /)).to_minparen_infix

http://www.rubyquiz.com/

3. Enjoy!

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

$ ruby postfix_to_infix.rb '56 34 213.7 + * 678 -'


56 * (34 + 213.7) - 678

$ ruby postfix_to_infix.rb '1 56 35 + 16 9 - / +'


1 + (56 + 35) / (16 - 9)

to these:

$ ruby postfix_to_infix.rb '56 34 213.7 + * 678 -'


((56 * (34 + 213.7)) - 678)

$ ruby postfix_to_infix.rb '1 56 35 + 16 9 - / +'
(1 + ((56 + 35) / (16 - 9)))

Posting equations and your output is not a spoiler.


____________________________________________________________________________________
Get easy, one-click access to your favorites.
Make Yahoo! your homepage.
http://www.yahoo.com/r/hs


Pawel Radecki

unread,
Dec 2, 2007, 12:14:10 PM12/2/07
to
Hello everybody,

Here's my solution. I know it's pretty long but I'm still learning...
BIG thanks for the quiz!

#!/usr/bin/env ruby

# Solution to Ruby Quiz #148 (see http://www.rubyquiz.com/quiz148.html)
# by Pawel Radecki (pawel.j...@gmail.com).

# Note1: There may be couple of different and correct postfix to infix
# transformations but this program only gives one and ignores others.
#
# Example:
# postfix: 1 2 + 4 * 5 + 3 -
# infix: (1 + 2) * 4 + 5 - 3
# infix: (1+2)*4+5-3
# infix: 5 + (1 + 2) * 4 - 3
# infix: 5 + 4 * (1 + 2) - 3
# ... (there are more)

# Note2: Unary operators not supported!

# Note3: I don't know why ^ in input argument doesn't work, anybody
knows...?

require 'logger'

$LOG = Logger.new($stderr)

#logging
#$LOG.level = Logger::DEBUG #DEBUG
$LOG.level = Logger::ERROR #PRODUCTION

class PostfixEquation < String

private
@@OPERATOR_PRIORITIES = {
'V' => 2,
'**' => 2,
'*' => 2,
'/' => 2,
'+' => 3,
'-' => 3
}

def normalize_expression (expression)
a = expression.split

$LOG.debug("array: #{a}")
$LOG.debug("#{a[0]} #{a[2]} #{a[1]}")

@operator_stack.push(a[2])

$LOG.debug("@operator_stack #{@operator_stack}")

s = "#{a[0]} #{a[2]} #{a[1]}"
if @operator_stack.length>1 &&
@@OPERATOR_PRIORITIES[@operator_stack[-2]]<@@OPERATOR_PRIORITIES[a[2]]
s = "(#{s})"
end

if @two_branches
@operator_stack.pop
end

#hack to resolve situation with two complex expressions forming
another
if a[0] =~/[a-z]+/ && a[1] =~/[a-z]+/
@two_branches = true
else
@two_branches = false
end

return s
end

public
def initialize(equation)
@string = super.to_str
@label = "a"
@expressions = {}
@operator_stack = []
@two_branches = false
end

def to_infix
$LOG.debug("string: #{@string}")

while (true)
$LOG.debug("string: #{@string}")
$LOG.debug("string lenth: #{@string.length}")

#look for "(number or letter(s)) (number or letter(s))
operator" pattern
#where number is chain of any digit and . (dot) characters
#letter(s) is one or more letters
#and operator is one of following characters: + - * / p r
@string =~
/([0-9\.]+|[a-z]+)\s([0-9\.]+|[a-z]+)\s((\*\*)|(V)|\+|\*|\/|-)/
$LOG.debug("pattern match: #{$`}<<#{$&}>>#{$'}")

# if the the expression is not valid
if ($&==nil || $&=="") && @string.length>1
$LOG.error("Stack is not empty!")
raise Exception.new("Stack is not empty!")
end

@expressions[@label] = $&
@string = "#{$`}#{@label}#{$'}"
$LOG.debug("string: #{@string}")
$LOG.debug("string lenth: #{@string.length}")

#if it's the last match
if $`=="" && $'=="" && @string.length==1
break
end

@label.succ!
$LOG.debug("label: #{@label}")
$LOG.debug("")

end

while (!@expressions.empty?)
@string.sub!(/([a-z])/) {
letter = $1.dup
s = String.new(normalize_expression(@expressions[letter]))
@expressions.delete(letter)
s
}
$LOG.debug("string: #{@string}")
$LOG.debug("")
end

return @string
end
end

USAGE = <<ENDUSAGE
Usage:
postfix_to_infinix.rb 'mathematical equation'

Valid binary operators: + - * / V **
where
V is a root operator, ex.: 5 V 1 means 5-th root of a number 1
** is a power operator, ex.: 2 ** 3 means power with base 2 and
exponent 3

(Unary operators not supported!)

Example:
postfix_to_infinix.rb '56 34 213.7 + ** 678 -'
ENDUSAGE

if ARGV.length!=1
puts USAGE
exit
end

e = PostfixEquation.new(ARGV[0])
begin
puts e.to_infix
rescue StandardError => err
puts err
end

--
Pawel Radecki
m: +48 695 34-64-76
e: pawel.j...@gmail.com
w: http://radeckimarch.blogspot.com/

Justin Ethier

unread,
Dec 2, 2007, 12:27:52 PM12/2/07
to
Note: parts of this message were removed by the gateway to make it a legal Usenet post.

Hello,

Here is my solution. It minimizes the use of parentheses, and can easily be
extended to support other operators in addition to +, -, * / as long as they
are evaluated from left-to-right:

# This class represents a single token on the infix stack.
# A token may be a single operator / number from the postfix expr, or a
portion of the infix expr being built.
class Token
# Accepts a token string and optionally the last operator added
def initialize(tok, last_op = nil)
@tok, @last_op = tok, last_op
end

# Determines if the current token is an operator
def is_op?
case @tok
when "+", "-", "*", "/"
return true
else
return false
end
end

# Defines the precedence of operators
def precedence(op)
case op
when "*", "/"
return 5
when "+", "-"
return 6
else
return nil
end
end

# Returns the token with parentheses added if they are needed for the
given op
def pack(op)
return "(#{tok})" if last_op != nil and (precedence(op) <
precedence(last_op))
return tok
end

attr_reader :tok, :last_op
end

# Module of Postfix ==> Infix conversion functions
module PostfixToInfix

# Main convertion function
def PostfixToInfix.translate(postfix)
stack, toks = [], postfix.split(" ").reverse

for tok in toks
stack << Token.new(tok)
process_stack(stack)
end

process_stack(stack) while stack.size > 1 # Finish stack processing
stack[0].tok
end

# Process the current postfix stack, converting to infix if there is
enough info
def PostfixToInfix.process_stack(stack)
while stack.size > 2 and not stack[-1].is_op? and not stack[-2].is_op?
eq = []
3.times{ eq << stack.pop }
op = eq[2].tok
tok = "#{eq[0].pack(op)} #{op} #{eq[1].pack(op)}"
stack << Token.new(tok, op)
end
end
end

Full Program: http://pastie.caboo.se/124320
Test Cases: http://pastie.caboo.se/124321

Thanks,

Justin

On Nov 30, 2007 8:28 AM, Ruby Quiz <ja...@grayproductions.net> wrote:

Lee Jarvis

unread,
Dec 2, 2007, 12:28:57 PM12/2/07
to
Well, here is my submission.. I truly was stuck with this so I just done
the most basic stuff, I will have a look through the other submissions a
bit later to see how others done it..

#!/usr/bin/ruby -w

argv = Array.new
if ARGV.empty?
puts "#{$0} <Postfix equation>"
else
ARGV.each do |a|
if ['+', '-', '/', '*'].include?(a)
last = argv.pop
first = argv.pop
argv << "(#{first} #{a} #{last})"
else
argv << a
end
end
puts argv
end


I will try and mod the code to remove some parenthesis.

Regards,
Lee
--
Posted via http://www.ruby-forum.com/.

tho_mica_l

unread,
Dec 2, 2007, 1:20:09 PM12/2/07
to
I'm sorry for spamming this list with my inferior solutions. But J
Koppel's approach made me to reconsider my solution, which now
supports custom output format (eg for Arrays), functions with 3+
arity, and functions for which the arity is defined by the last
argument on the stack.

Thomas.

class Quiz148
class << self
def run(args)
iqueue = args.map {|e| e.split(/\s+/)}.flatten
return Quiz148.new(iqueue).process
end
end

def initialize(iqueue)
@iqueue = iqueue
@depth = 0
@stack = []
@ops = {
'+' => [10],

'-' => [10, 2, nil, [[Object, nil], [String,
'(%s)']]],


'*' => [5],
'/' => [5],
'%' => [5],

'<<' => [3],
'^' => [5, 2, nil, [[String, '(%s)'],
[Numeric, nil]]],
'**' => [5, 2, nil, [[String, '(%s)'],
[Numeric, nil]]],
'sqrt' => [0, 1, '#{op}(#{vals})'],
'binom' => [0, 2, '#{op}(#{vals.join(\', \')})'],
'sum3' => [0, 3],
'Array' => [0, -1, '[#{vals.join(\', \')}]'],
}
@opnames = @ops.keys
end

def process


@iqueue.each do |token|
if @opnames.include?(token)
op = token

opp, arity, fmt, *patterns = @ops[op]
case arity
when -1
aop, arity = @stack.pop
when nil
arity = 2
end
case arity
when 1
fmt ||= '#{op}#{vals}'
when 2
fmt ||= '#{vals.join(\' \' + op + \' \')}'
else
fmt ||= '#{op}(#{vals.join(\', \')})'
end
vals = (1..arity).inject([]) {|a, i| a <<
@stack.pop}.reverse
idx = 0
vals.map! do |aop, val|
if aop
aopp, *aopatterns = @ops[aop]
if opp > 0 and aopp > opp
val = '(%s)' % val
end
end
patterns.each do |pattern|
p, e = pattern[idx]
if (aop.nil? or aopp > 0) and val.kind_of?(p)
if e
val = e % val
end
break
end
end
idx += 1
val
end
@stack << [op, eval("\"#{fmt}\"")]
else
@stack << [nil, eval(token)]


end
end
# The stack should include only one element here. A check
would
# be necessary.

o, v = @stack.pop
v
end
end

puts Quiz148.run('1 2 2 binom 3 2 ^ sum3') == 'sum3(1,
binom(2, 2), 3 ^ 2)'
puts Quiz148.run('1 2 2 binom 3 3 Array') == '[1, binom(2,
2), 3]'
puts Quiz148.run('1 2 3 3 Array 4 <<') == '[1, 2, 3] <<
4'
puts Quiz148.run('1 2 3 3 Array 4 2 * <<') == '[1, 2, 3] <<
(4 * 2)'

Ken Bloom

unread,
Dec 2, 2007, 1:38:05 PM12/2/07
to
On Sun, 02 Dec 2007 12:14:10 -0500, Pawel Radecki wrote:

> Hello everybody,
>
> Here's my solution. I know it's pretty long but I'm still learning...
> BIG thanks for the quiz!
>
>
>
> #!/usr/bin/env ruby
>
> # Solution to Ruby Quiz #148 (see http://www.rubyquiz.com/quiz148.html)
> # by Pawel Radecki (pawel.j...@gmail.com).
>
> # Note1: There may be couple of different and correct postfix to infix #
> transformations but this program only gives one and ignores others. #
> # Example:
> # postfix: 1 2 + 4 * 5 + 3 -
> # infix: (1 + 2) * 4 + 5 - 3
> # infix: (1+2)*4+5-3
> # infix: 5 + (1 + 2) * 4 - 3
> # infix: 5 + 4 * (1 + 2) - 3
> # ... (there are more)
>
> # Note2: Unary operators not supported!
>
> # Note3: I don't know why ^ in input argument doesn't work, anybody
> knows...?

It appears to be this regex over here. You need to escape the ^ if you
want to use it in a regex, because ^ signals the beginning of a line in a
regex.

> @string =~
> /([0-9\.]+|[a-z]+)\s([0-9\.]+|[a-z]+)\s((\*\*)|(V)|\+|\*|\/|-)/

--Ken

Daniel Martin

unread,
Dec 2, 2007, 2:16:18 PM12/2/07
to
Ruby Quiz <ja...@grayproductions.net> writes:

> This week's quiz is to write a script that translates postfix
> expressions into the equivalent infix expression.

#! /usr/bin/env ruby
#
# Accepts RPN using the basic four operations + - / *, as well as
# ^ to denote exponentiation, and outputs infix notation with the
# minimum number of parentheses. Note that infix exponentiation
# associates to the right, so 2 ^ 2 ^ 2 == 2 ^ (2 ^ 2)

instr = ARGV.join(' ');
s = instr.dup
s.gsub!(/(\d+(\.\d*)?)/, '<n:\1>')
s.gsub!(/\s/,'')

# Data structures? We don't need no stinkin' data structures.
# Postfix expression to infix expression via regular expressions.

while s =~ /<.*</ do
f = false
f |= s.gsub!(%r{<.:([^>]*)><.:([^>]*)>\+}, '<+:\1 + \2>')

f |= s.gsub!(%r{<.:([^>]*)><[+-]:([^>]*)>-}, '<-:\1 - (\2)>')
f |= s.gsub!(%r{<.:([^>]*)><[^+-]:([^>]*)>-}, '<-:\1 - \2>')

f |= s.gsub!(%r{<[+-]:([^>]*)><[+-]:([^>]*)>\*}, '<*:(\1) * (\2)>')
f |= s.gsub!(%r{<[+-]:([^>]*)><[^+-]:([^>]*)>\*}, '<*:(\1) * \2>')
f |= s.gsub!(%r{<[^+-]:([^>]*)><[+-]:([^>]*)>\*}, '<*:\1 * (\2)>')
f |= s.gsub!(%r{<[^+-]:([^>]*)><[^+-]:([^>]*)>\*}, '<*:\1 * \2>')

f |= s.gsub!(%r{<[+-]:([^>]*)><[*/+-]:([^>]*)>/}, '</:(\1) / (\2)>')
f |= s.gsub!(%r{<[^+-]:([^>]*)><[*/+-]:([^>]*)>/}, '</:\1 / (\2)>')
f |= s.gsub!(%r{<[+-]:([^>]*)><[^*/+-]:([^>]*)>/}, '</:(\1) / \2>')
f |= s.gsub!(%r{<[^+-]:([^>]*)><[^*/+-]:([^>]*)>/}, '</:\1 / \2>')

f |= s.gsub!(%r{<[^n]:([^>]*)><[^n^]:([^>]*)>\^}, '<^:(\1) ^ (\2)>')
f |= s.gsub!(%r{<[^n]:([^>]*)><[n^]:([^>]*)>\^}, '<^:(\1) ^ \2>')
f |= s.gsub!(%r{<n:([^>]*)><[^n^]:([^>]*)>\^}, '<^:\1 ^ (\2)>')
f |= s.gsub!(%r{<n:([^>]*)><[n^]:([^>]*)>\^}, '<^:\1 ^ \2>')
unless f
raise "Malformed RPN string: '#{instr}' (s is #{s})"
end
end

s.gsub!(/<.:(.*)>/, '\1')
puts s

__END__

Adam Shelly

unread,
Dec 2, 2007, 3:02:49 PM12/2/07
to
On Nov 30, 2007 5:28 AM, Ruby Quiz <ja...@grayproductions.net> wrote:
> This week's quiz is to write a script that translates postfix expressions into
> the equivalent infix expression.

Here's mine.

It removes parenthesis, and handles all the binary operators in Ruby
(unless I missed some).
You can use a different set of operators and precedence rules by
changing the $Operators hash.

#postfix to infix
# ruby quix #148
# Adam Shelly
#
# Converts postfix to infix notation
# uses ruby's operators & precedence rules

$Operators = {
'&&'=>0, '||'=>0,
'=='=>1, '==='=>1, '<=>'=>1,
'<='=>2, '>='=>2, '<' =>2, '>'=>2,
'^' =>3, '|' =>3,
'&' =>4,
'<<'=>5, '>>'=>5,
'+' =>6, '-' =>6,
'*' =>7, '/' =>7, '%'=> 7,
'**'=>8,
:term=>10
}

class Term
attr_reader :precedence
def initialize str, groupPrec=nil
@s = str
@precedence = $Operators[str]||groupPrec||$Operators[:term]
end
def isOp
@precedence != $Operators[:term]
end
def parenthesize
@s="(#{@s})"
end
def to_s
@s
end
end

class Infix
def initialize rpn
stack=[]
rpn.split.each do |t|
term = Term.new(t)
if term.isOp
lval = stack.pop
rval = stack.pop
raise "Empty Stack" unless lval && rval
lval.parenthesize if lval.precedence < term.precedence
rval.parenthesize if rval.precedence < term.precedence
phrase = "#{rval} #{term} #{lval}"
tok = Token.new(phrase,term.precedence)
# p term
end
stack.push term
end
@expr = stack.pop
raise "Extra terms" unless stack.size==0
end
def to_s
@expr
end
end

if __FILE__ == $0
puts Infix.new(ARGV.join(' ')).to_s
end

-Adam

Adam Shelly

unread,
Dec 2, 2007, 3:09:24 PM12/2/07
to
On Dec 2, 2007 12:02 PM, Adam Shelly <adam....@gmail.com> wrote:
> On Nov 30, 2007 5:28 AM, Ruby Quiz <ja...@grayproductions.net> wrote:
> > This week's quiz is to write a script that translates postfix expressions into
> > the equivalent infix expression.
>
> Here's mine.

I should know better than to try to cleanup my code, and then submit
it without running it first.

This line:
> tok = Token.new(phrase,term.precedence)
should read
term = Term.new(phrase, term.precedence)

-Adam

Alex Shulgin

unread,
Dec 2, 2007, 3:19:24 PM12/2/07
to
On Nov 30, 3:28 pm, Ruby Quiz <ja...@grayproductions.net> wrote:
>
> -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
>
> There are many different ways to write mathematical equations. Infix notation
> is probably the most popular and yields expressions like:
>
> 2 * (3 + 5)
>
> Some people like to work with a postfix notation (often called Reverse Polish
> Notation or just RPN) though, which doesn't require parentheses for the same
> equation:
>
> 2 3 5 + *

#!/usr/bin/ruby

$prec_tbl = {
['*', '+'] => true,
['*', '-'] => true,
['/', '+'] => true,
['/', '-'] => true,
['-', '-'] => true,
['/', '/'] => true
}

def precede?(top, op)
$prec_tbl[[top, op]]
end

def infix(arr, top = nil)
throw "invalid postfix expression" unless !arr.empty?
op = arr.pop
if op =~ /\+|\-|\*|\//
right = infix(arr, op)
left = infix(arr, op)
par = precede?(top, op)
(par ? "(" : "") + "#{left} #{op} #{right}" + (par ? ")" : "")
else
op
end
end

STDIN.each do |line|
arr = line.split(/\s+/)
begin
res = infix(arr)
throw "invalid postfix expression" unless arr.empty?
puts "#{res} => #{eval(res)}"
rescue
STDERR.puts $!
end
end


--
Alex Shulgin

Daniel Martin

unread,
Dec 2, 2007, 3:54:12 PM12/2/07
to
"Adam Shelly" <adam....@gmail.com> writes:

> I should know better than to try to cleanup my code, and then submit
> it without running it first.
>
> This line:
>> tok = Token.new(phrase,term.precedence)
> should read
> term = Term.new(phrase, term.precedence)

Even with that cleanup, it doesn't seem to work:

esau:/tmp$ ruby asrq148.rb '2 3 - 5 +'
+
esau:/tmp$ ruby asrq148.rb '2 3 -'
-

Alex Shulgin

unread,
Dec 2, 2007, 3:55:01 PM12/2/07
to
On Dec 2, 10:19 pm, Alex Shulgin <alex.shul...@gmail.com> wrote:
>
> #!/usr/bin/ruby
>
> $prec_tbl = {
> ['*', '+'] => true,
> ['*', '-'] => true,
> ['/', '+'] => true,
> ['/', '-'] => true,
> ['-', '-'] => true,
> ['/', '/'] => true

Um... I think, I'm missing this here:

['/', '*'] => true

>
> }
[snip]


>
> STDIN.each do |line|
> arr = line.split(/\s+/)
> begin
> res = infix(arr)
> throw "invalid postfix expression" unless arr.empty?
> puts "#{res} => #{eval(res)}"

And it chokes on division by zero due to the eval() w/o printing the
infix form... :-/

> rescue
> STDERR.puts $!
> end
> end

Anyway I think it addresses the basic problem. Nice quiz! :-)


--
Alex

Eugene Kalenkovich

unread,
Dec 2, 2007, 5:12:57 PM12/2/07
to
Finally we got a quiz with 'less than 20 (minutees, loc)' rule satisfied :)

Here is my solution:

>--------------------------------------------------
a=ARGV[0].split

pri='+-*/'

prs=[]
i=0

while a.length>1 do
if pr=pri.index(op=a[i])
raise if i<2
a[i-2]='('+a[i-2]+')' if pr>>1 > prs[i-2]
a[i-1]='('+a[i-1]+')' if pr>>1 > prs[i-1] || (pr>>1 == prs[i-1] &&
pr&1==1)
a[i-2,3]=a[i-2]+op+a[i-1]
prs[i-=2]=pr>>1
else
prs[i]=4
end
i+=1
end rescue a[0]="invalid expression"

puts a[0]
>--------------------------------------------------


Voroztsov Artem

unread,
Dec 2, 2007, 8:00:45 PM12/2/07
to
Good day, everybody!

I've added the problem to online contest http://acm.mipt.ru/judge.

http://acm.mipt.ru/judge/problems.pl?problem=126&lang=en

Now you can check yourself!

Sorry, I will use 'gets' instead of 'ARGV'.

#########################################
# OK
# Let's start with simple one.
# This one just does the job without removing odd parentheses

stack = []
gets.strip.split.each do |token|
case token
when '*', '+', '/', '-'
stack << [')', stack.pop, token, stack.pop, '('].reverse!
else
stack << token
end
end

puts stack.flatten.join

########################################
# Now let's do the thing we are here for.
# We will use idea of operator strength.
# Each operator has left and right strength.
# Binary operation should "protect" itself with parentheses if there
is stronger operator
# to the left or to the right. Two neighbor operators affect each
other with strengths:
# one with left-strength (the one to the right) and another with right-strength
# (the one to the left)
#
OP_STRENGTH = {
:left => {'+'=>2, '-'=>2, '*'=>4, '/'=>4},
:right => {'+'=>2, '-'=>3, '*'=>4, '/'=>5}
}

stack = []
gets.strip.split.each do |token|
# puts "TOKEN '#{token.inspect}'"
case token
when '*', '+', '/', '-'
stack << [stack.pop, token, stack.pop].reverse!
else
stack << token
end
end

# Uncomment these line to see some sort of 'parse tree'
# require 'yaml'
# puts stack.to_yaml

def parenthesize(triplet, top_op_strength, side)
if triplet.is_a? Array
parenthesize(triplet[0], OP_STRENGTH[:left][triplet[1]], :right)
parenthesize(triplet[2], OP_STRENGTH[:right][triplet[1]], :left)
if OP_STRENGTH[side][triplet[1]] < top_op_strength
triplet.push ')'
triplet.unshift '('
end
end
end

parenthesize(stack.last, 0, :right)

puts stack.flatten.join

#########################################
#
#
# Lets try the previous version with input
# '0 ' + (1..N-1).to_a.join(' - ') + ' -',
# for N = 15000, 30000, 60000
# We will see two thins
# 1) in `parenthesize': stack level too deep (SystemStackError)
# 2) time grows quadratically. But why? The bad guy is 'flatten'!
# First of all we should get rid of recursion:

def parenthesize(triplet, top_op_strength, side)
return unless triplet.is_a?(Array)
q = [ [triplet, top_op_strength, side] ]
while !q.empty?
t,top_op_strength,side = q.pop
q << [t[0], OP_STRENGTH[:left][t[1]], :right] if t[0].is_a?(Array)
q << [t[2], OP_STRENGTH[:right][t[1]], :left] if t[2].is_a?(Array)
if OP_STRENGTH[side][t[1]] < top_op_strength
t.push ')'
t.unshift '('
end
end
end
#########################################
#
# The previous version still work O( L^2), where L is number of
tokens in input expression.
# Let's get rid of 'flatten':
#
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

parenthesize(stack.last, 0, :right)
puts
############################################
#
# And finally, one may prefer Hash version of parse-tree (though
it's a little bit slower):
OP_STRENGTH = {
:left => {'+'=>2, '-'=>2, '*'=>4, '/'=>4},
:right => {'+'=>2, '-'=>3, '*'=>4, '/'=>5}
}

stack = []
gets.strip.split.each do |token|
case token
when '*', '+', '/', '-'
stack << {:r=>stack.pop, :op=>token, :l=>stack.pop}
else
stack << token
end
end

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?(Hash)
if OP_STRENGTH[side][t[:op]] < top_op_strength
print '('
q << ')'
end
q << [t[:r], OP_STRENGTH[:right][t[:op]], :left]
q << t[:op]
q << [t[:l], OP_STRENGTH[:left][t[:op]], :right]
else
print t
end
end
end

parenthesize(stack.last, 0, :right)
puts
############################################

Final remarks.

1. Defining left -and right- operator strengths allows us to take into
account different aspects
1) priority
2) commutativity/ non commutativity
3) associativity type (left or right)

2. We discovered problem with Array#flatten. Is it a known issue?
(I use ruby 1.8.6 (2007-03-13 patchlevel 0) [i386-mswin32])

For N = 10000, 20000 and input = '0 ' + (1..N-1).to_a.join(' - ') + ' -'

require 'benchmark'
puts Benchmark.measure {
stack.flatten
}
return the following results:

N time
5000 1.265000
10000 5.141000
20000 20.484000

So, it's quadratic.
While final solution works less than 1 second (0.079 seconds).
What's the problem with flatten?

Voroztsov Artem

unread,
Dec 2, 2007, 8:33:44 PM12/2/07
to
2007/12/3, Voroztsov Artem <artem.v...@gmail.com>:

> 2. We discovered problem with Array#flatten. Is it a known issue?
> (I use ruby 1.8.6 (2007-03-13 patchlevel 0) [i386-mswin32])
>
> For N = 10000, 20000 and input = '0 ' + (1..N-1).to_a.join(' - ') + ' -'
>
> require 'benchmark'
> puts Benchmark.measure {
> stack.flatten
> }
> return the following results:
>
> N time
> 5000 1.265000
> 10000 5.141000
> 20000 20.484000
>
> So, it's quadratic.
> While final solution works less than 1 second (0.079 seconds).
> What's the problem with flatten?
>
>


So, here is the code just about 'flatten', not the QUIZ issue.
I write self-made 'flatten' and it works much faster for the
given case and many other cases we get in the quiz problem.

I use ruby 1.8.6 (2007-03-13 patchlevel 0) [i386-mswin32]

Can anybody check it for ruby 1.9.1?

#!/usr/bin/ruby

N = 10000

inputs = []
inputs << '0 ' + (1..N-1).to_a.join(' - ') + ' -'
inputs << (1..N).to_a.join(' ') + (" +" * (N-1))

class Array
def flatten2
res = []
x = self.reverse
while !x.empty?
a = x.pop
if a.is_a?(Array)
x.push(*a)
else
res << a
end
end
res.reverse
end
end

require 'benchmark'

inputs.each do |input|
stack = []
input.strip.split.each do |token|


# puts "TOKEN '#{token.inspect}'"
case token
when '*', '+', '/', '-'
stack << [stack.pop, token, stack.pop].reverse!
else
stack << token
end
end

res1 = res2 = nil

Benchmark.bm {|b|
b.report('original flatten') {
res1 = stack.flatten
}
b.report('self-made flatten') {
res2 = stack.flatten2
}
}
puts res1 == res2
end


I have the following output:

>ruby test_flatten.rb
user system total real
original flatten 5.125000 0.000000 5.125000 ( 5.141000)
self-made flatten 0.032000 0.000000 0.032000 ( 0.031000)
true
user system total real
original flatten 5.063000 0.000000 5.063000 ( 5.079000)
self-made flatten 0.031000 0.000000 0.031000 ( 0.031000)
true

Eric Mahurin

unread,
Dec 2, 2007, 10:23:23 PM12/2/07
to
Note: parts of this message were removed by the gateway to make it a legal Usenet post.

On Nov 30, 2007 7:28 AM, Ruby Quiz <ja...@grayproductions.net> wrote:

> For an added bonus, try to keep the parentheses added to infix expressions
> to
> the minimum of what is needed.
>

My solution does the above, plus a few more things:

* maintains an OO data structure (to do everything below)
* further reduce parentheses (and post-fix stack depth) by using some
associativity
* evaluates the result
* gives stack-reduced post-fix form

The basic idea of the solution is to have an object for each expression
(possibly with sub-expressions as operands) and have methods for applying
another operation in either direction (i.e. have both #add and #radd -
reverse add). This allows independent decisions on what each type of
expression should do when it is in either operand of another operation.

Here are a few examples (result shows internal postfix, infix, and result):

>ruby quiz148.rb "2 3 5 + *"
2 3 5 + * => 2*(3 + 5) => 16
>ruby quiz148.rb "56 34 213.7 + * 678 -"
56 34 213.7 + * 678 - => 56*(34 + 213.7) - 678 => 13193.2
>ruby quiz148.rb "1 56 35 + 16 9 - / +"
1 56 35 + 16 9 - / + => 1 + (56 + 35)*(16 - 9) => 14
>ruby quiz148.rb "1 2 3 4 5 + + + +"
1 2 + 3 + 4 + 5 + => 1 + 2 + 3 + 4 + 5 => 15
>ruby quiz148.rb "1 2 3 4 5 - - - -"
1 2 - 3 + 4 - 5 + => 1 - 2 + 3 - 4 + 5 => 3

Notice the last two. The internal postfix is different compared to the
original. It is better because when evaluating on a stack, less stack space
is needed (old max depth:5, new:2).

This architecture would also allow for further optimizations.

#!/usr/bin/env ruby

class Atom
def initialize(arg)
@data = arg
end
def to_s
@data.to_s
end
def to_a
[@data]
end
def eval
Kernel.eval(@data)
end
def radd(other)
other.add(self)
end
def add(other)
Sum.new(self, other)
end
def rsub(other)
other.sub(self)
end
def sub(other)
Difference.new(self, other)
end
def rmul(other)
other.mul(self)
end
def mul(other)
Product.new(self, other)
end
def rdiv(other)
other.div(self)
end
def div(other)
Quotient.new(self, other)
end
end

class Group < Atom
def initialize(expr)
@expr = expr
end
def to_s
"(#{@expr})"
end
def to_a
@expr.to_a
end
def eval
@expr.eval
end
end

class Sum < Atom
def initialize(left, right)
@left = left
@right = right
end
def to_s
"#{@left} + #{@right}"
end
def to_a
@left.to_a.concat(@right.to_a) << :+
end
def eval
@left.eval + @right.eval
end
def radd(other)
@left.radd(other).add(@right)
end
def rsub(other)
@left.rsub(other).sub(@right)
end
def rmul(other)
other.mul(Group.new(self))
end
def mul(other)
Product.new(Group.new(self), other)
end
def rdiv(other)
other.div(Group.new(self))
end
def div(other)
Quotient.new(Group.new(self), other)
end
end

class Difference < Sum
def to_s
"#{@left} - #{@right}"
end
def to_a
@left.to_a.concat(@right.to_a) << :-
end
def eval
@left.eval - @right.eval
end
def radd(other)
@left.radd(other).sub(@right)
end
def rsub(other)
@left.rsub(other).add(@right)
end
end

class Product < Atom
def initialize(left, right)
@left = left
@right = right
end
def to_s
"#{@left}*#{@right}"
end
def to_a
@left.to_a.concat(@right.to_a) << :*
end
def eval
@left.eval * @right.eval
end
def rmul(other)
@left.rmul(other).mul(@right)
end
def rdiv(other)
# could do this to reduce grouping and stack depth
# but this will increase expensive divisions
# @left.rdiv(other).div(@right)
other.div(Group.new(self))
end
end

class Quotient < Product
def to_s
"#{@left}*#{@right}"
end
def to_a
@left.to_a.concat(@right.to_a) << :/
end
def eval
@left.eval / @right.eval
end
def rmul(other)
@left.rmul(other).div(@right)
end
def rdiv(other)
@left.rdiv(other).mul(@right)
end
end

stack = []
ARGV.each { |arg|
arg.scan(/\S+/) { |token|
case token
when "+" : stack.push(stack.pop.radd(stack.pop))
when "-" : stack.push(stack.pop.rsub(stack.pop))
when "*" : stack.push(stack.pop.rmul(stack.pop))
when "/" : stack.push(stack.pop.rdiv(stack.pop))
else ; stack.push(Atom.new(token))
end
}
}

stack.each { |expr|
puts("#{expr.to_a.join(' ')} => #{expr} => #{expr.eval}")
}

Ken Bloom

unread,
Dec 2, 2007, 11:38:05 PM12/2/07
to
Solutions that minimize parentheses must work with the following test
case:
"3 5 * 5 8 * /" should turn into "3 * 5 / (5 * 8)" not "3 * 5 / 5 * 8"
similarly for
"3 5 + 5 8 + -" which should turn into "3 + 5 - (5 + 8)"

Most of the solutions posted so far get this wrong (I haven't checked
exhaustively). A few that notably get it correct (while in some way
minimzing parentheses):
* Daniel Martin
* Robert Dober

Eugene Kalenkovich

unread,
Dec 2, 2007, 11:51:33 PM12/2/07
to
"Ken Bloom" <kbl...@gmail.com> wrote in message
news:pan.2007.12...@gmail.com...

> Solutions that minimize parentheses must work with the following test
> case:
> "3 5 * 5 8 * /" should turn into "3 * 5 / (5 * 8)" not "3 * 5 / 5 * 8"
> similarly for
> "3 5 + 5 8 + -" which should turn into "3 + 5 - (5 + 8)"
>
> Most of the solutions posted so far get this wrong (I haven't checked
> exhaustively). A few that notably get it correct (while in some way
> minimzing parentheses):
> * Daniel Martin
> * Robert Dober
>
Hmm.. Please add my solution to the list :)


Eric DUMINIL

unread,
Dec 3, 2007, 4:18:33 AM12/3/07
to
As soon as pastie works again, I'll get my script back and ask you to
add my solution to the list!

Robert Dober

unread,
Dec 3, 2007, 5:13:58 AM12/3/07
to
On Dec 2, 2007 11:33 PM, Konrad Meyer <kon...@tylerc.org> wrote:
> Quoth Ruby Quiz:

>
> > The three rules of Ruby Quiz:
> >
> > 1. Please do not post any solutions or spoiler discussion for this quiz
> until
> > 48 hours have passed from the time on this message.
> >
> > 2. Support Ruby Quiz by submitting ideas as often as you can:
> >
> > http://www.rubyquiz.com/
> >
> > 3. Enjoy!
> >
> > Suggestion: A [QUIZ] in the subject of emails about the problem helps
> everyone
> > on Ruby Talk follow the discussion. Please reply to the original quiz
> message,
> > if you can.
> >
> > -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
> >
> > There are many different ways to write mathematical equations. Infix
> notation
> > is probably the most popular and yields expressions like:
> >
> > 2 * (3 + 5)
> >
> > Some people like to work with a postfix notation (often called Reverse
> Polish
> > Notation or just RPN) though, which doesn't require parentheses for the same
> > equation:
> >
> > 2 3 5 + *
> >
> > You can compare the results of these equations using the Unix utilities bc
> > (infix) and dc (postfix):
> >
> > $ bc <<< '2 * (3 + 5)'
> > 16
> > $ dc <<< '2 3 5 + * p'
> > 16
> >
> > The "p" instruction tacked onto the end of the expression for dc just tells
> it
> > to print the result.
> >
> > This week's quiz is to write a script that translates postfix expressions
> into
> > the equivalent infix expression. In the simplest form, your script should
> > function as such:
> >
> > $ ruby postfix_to_infix.rb '2 3 +'
> > 2 + 3
> >
> > At minimum, try to support the four basic math operators: +, -, *, and /.
> Feel
> > free to add others though. For numbers, remember to accept decimal values.
> >
> > You can count on the postfix expressions having spaces between each term, if
> you
> > like. While dc is content with 2 3+p, you don't have to support it unless
> you
> > want to.
> >
> > For an added bonus, try to keep the parentheses added to infix expressions
> to
> > the minimum of what is needed. For example, prefer these results:
> >
> > $ ruby postfix_to_infix.rb '56 34 213.7 + * 678 -'

> > 56 * (34 + 213.7) - 678
> > $ ruby postfix_to_infix.rb '1 56 35 + 16 9 - / +'

> > 1 + (56 + 35) / (16 - 9)
> >
> > to these:
> >
> > $ ruby postfix_to_infix.rb '56 34 213.7 + * 678 -'
> > ((56 * (34 + 213.7)) - 678)
> > $ ruby postfix_to_infix.rb '1 56 35 + 16 9 - / +'
> > (1 + ((56 + 35) / (16 - 9)))
> >
> > Posting equations and your output is not a spoiler.
>
> The obvious solution to removing all unnecessary parentheses is to remove all
> possible permutations of them from a string, eval() them all, and check to
> make sure they get the right number :-).
>
> Just kidding,
Look at my third solution, no idea is too stupid not to be put at work ;)

Cheers
Robert
> --
> Konrad Meyer <kon...@tylerc.org> http://konrad.sobertillnoon.com/
>

--

http://ruby-smalltalk.blogspot.com/

---
All truth passes through three stages. First, it is ridiculed. Second,
it is violently opposed. Third, it is accepted as being self-evident.
Schopenhauer (attr.)

Михаил Горбунов

unread,
Dec 3, 2007, 6:59:29 AM12/3/07
to
Note: parts of this message were removed by the gateway to make it a legal Usenet post.

This is my solution:

# this program understands simple operations and also sin, cos, tg and ctg
# to use, provide string in BPN as argument
# example: ruby postf2inf '3 5 + sin 5 3 cos + /'
# result in "sin(3 + 5) / (5 + cos(3))"
str_postf=ARGV[0]
str_inf=[]
prior = {'+'=>1,'-'=>1,'*'=>3,'/'=>3}
stack = str_postf.split(' ')

0.upto stack.size-1 do |l|
if ['+','-','*','/'].include?(stack[l])
arg1 = str_inf.pop
arg2 = str_inf.pop
arg1[1]='('+arg1[1]+')' if arg1[0]<prior[stack[l]]
arg2[1]='('+arg2[1]+')' if arg2[0]<prior[stack[l]]
str_inf.push([prior[stack[l]] , arg2[1]+" #{stack[l]} "+arg1[1]])
elsif ['sin','cos','tg','ctg'].include?(stack[l])
str_inf.push([5 , "#{stack[l]}(#{str_inf.pop[1]})"])
else
str_inf.push([5,stack[l]])
end
end

p str_inf.pop[1]

Михаил Горбунов

unread,
Dec 3, 2007, 7:13:28 AM12/3/07
to
Note: parts of this message were removed by the gateway to make it a legal Usenet post.

Sorry, in previous post I forgot about parenthess in case of - and /.

# this program understands simple operations and also sin, cos, tg and ctg

# to use, provide string in RPN as argument


# example: ruby postf2inf '3 5 + sin 5 3 cos + /'
# result in "sin(3 + 5) / (5 + cos(3))"

str_postf=ARGV[0]
str_inf=[]
prior = {'+'=>1,'-'=>1,'*'=>3,'/'=>3}
stack = str_postf.split(' ')

0.upto stack.size-1 do |l|
if ['+','-','*','/'].include?(stack[l])
arg1 = str_inf.pop
arg2 = str_inf.pop
arg1[1]='('+arg1[1]+')' if arg1[0]<prior[stack[l]]

arg1[1]='('+arg1[1]+')' if ['-','/'].include?(stack[l]) and
arg1[0]==prior[stack[l]]

tho_mica_l

unread,
Dec 3, 2007, 7:39:11 AM12/3/07
to
This 4th version also passes K Bloom's testcases.

Like some other solutions The converter is itself a stack-based
interpreter. Instead of evaluating the expression, the expression gets
transformed.


# The converter is itself a stack-based interpreter. Instead of
# evaluating the expression, the expression gets transformed.


class Quiz148
class << self
def run(args)
iqueue = args.map {|e| e.split(/\s+/)}.flatten
return Quiz148.new(iqueue).process
end

def test(input, expected)
observed = run(input)
ok = observed == expected
if ok
print '.'
else
puts "\n%s != %s\n" % [expected, observed]
end
end
end

def initialize(iqueue)
# Tokenized input
@iqueue = iqueue
# The stack of [operator, element] tuples
@stack = []
# Pre-defined operators
# Fields:
# - precedence
# - arity
# - left associative
# - right associative
# - template
@ops = {
'+' => [10, 2, true, true],
'-' => [10, 2, true, false],
'*' => [5, 2, true, false],
'/' => [5, 2, true, false],
'%' => [5, 2, true, false],
'<<' => [3, 2, true, false],
'^' => [5, 2, false, true],
'**' => [5, 2, false, true],
'sqrt' => [0, 1, true, true, '#{op}(#{vals})'],
'mean' => [0, 2, true, true, '#{op}


(#{vals.join(\', \')})'],

'sum3' => [0, 3, true, true],
'Array' => [0, -1, true, true, '[#{vals.join(\',


\')}]'],
}
@opnames = @ops.keys
end

def process
@iqueue.each do |token|

# Check whether the token is an operator.


if @opnames.include?(token)
op = token

opp, arity, assoc_left, assoc_right, fmt = @ops[op]
case arity
when -1
ap, arity = @stack.pop


when nil
arity = 2
end
case arity
when 1
fmt ||= '#{op}#{vals}'
when 2
fmt ||= '#{vals.join(\' \' + op + \' \')}'
else
fmt ||= '#{op}(#{vals.join(\', \')})'
end

vals = []
# Get the arguments.
arity.times do
if @stack.empty?
puts 'Malformed expression: too few argument'
end
vals.unshift(@stack.pop)
end
idx = 0
# Rewrite the operator's arguments.
vals.map! do |ap, val|
# If opp is <= 0, the operator is a function and
we
# can ignore precedence values. If the value is
an
# atom, ditto.
if ap and opp > 0
app, *rest = @ops[ap]
# If the other operator is a function, it's
considered atomic.
if app > 0
# Put the value in parentheses if the
# operator isn't left or right-
associative
# of if the other operator's precedence
is
# greater than the current operator's one.
if (idx == 0 and !assoc_left) or (idx == 1
and !assoc_right) or app > opp


val = '(%s)' % val
end
end

end
idx += 1
val
end

# Format the values.


@stack << [op, eval("\"#{fmt}\"")]
else
@stack << [nil, eval(token)]
end
end

o, v = @stack.pop
unless @stack.empty?
puts 'Malformed expression: too many argument'
end
v
end
end

if __FILE__ == $0
if ARGV.empty?

Quiz148.test('2 3 +', '2 + 3')
Quiz148.test('56 34 213.7 + * 678 -', '56 * (34 + 213.7) -
678')
Quiz148.test('1 56 35 + 16 9 - / +', '1 + (56 + 35) / (16 -
9)')
Quiz148.test('1 2 + 3 4 + +', '1 + 2 + 3 + 4')
Quiz148.test('1 2 - 3 4 - -', '1 - 2 - (3 - 4)')
Quiz148.test('1 3 4 - -', '1 - (3 - 4)')
Quiz148.test('2 2 ^ 2 ^', '(2 ^ 2) ^ 2')
Quiz148.test('2 2 2 ^ ^', '2 ^ 2 ^ 2')
Quiz148.test('2 sqrt 2 2 ^ ^', 'sqrt(2) ^ 2 ^ 2')
Quiz148.test('2 3 2 2 ^ ^ sqrt 3 + *', '2 * (sqrt(3 ^ 2 ^ 2) +
3)')
Quiz148.test('2 3 mean 2 2 ^ ^', 'mean(2, 3) ^ 2 ^ 2')
Quiz148.test('1 2 3 2 2 ^ ^ mean + 3 *', '(1 + mean(2, 3 ^ 2 ^
2)) * 3')
Quiz148.test('2 3 2 2 ^ ^ mean', 'mean(2, 3 ^ 2 ^ 2)')
Quiz148.test('1 2 2 mean 3 2 ^ sum3', 'sum3(1, mean(2, 2), 3 ^
2)')
Quiz148.test('1 2 2 mean 3 3 Array', '[1, mean(2, 2), 3]')
Quiz148.test('1 2 3 3 Array 4 <<', '[1, 2, 3] << 4')
Quiz148.test('1 2 3 3 Array 4 2 * <<', '[1, 2, 3] << (4 * 2)')
Quiz148.test('1 1 Array 1 2 3 3 Array 4 2 * << -', '[1] - ([1,
2, 3] << (4 * 2))')
Quiz148.test('3 5 * 5 8 * /', '3 * 5 / (5 * 8)')
Quiz148.test('3 5 + 5 8 + -', '3 + 5 - (5 + 8)')

Ken Bloom

unread,
Dec 2, 2007, 11:43:33 PM12/2/07
to

This doesn't look right.
3 5 * 5 8 * / => 3*5*(5*8) => 0

Eric Mahurin

unread,
Dec 3, 2007, 9:42:10 AM12/3/07
to
Note: parts of this message were removed by the gateway to make it a legal Usenet post.

> Thanks Ken,

I obviously didn't test divide. My previously solution has a stupid typo in
Quotient#to_s. Should be:

class Quotient < Product
def to_s

"#{@left}/#{@right}" # had a * operator here before, whoops!
end
...

Here's a couple more tests:

>ruby quiz148.rb "3 5 / 5 8 / /"
3 5 / 5 / 8 * => 3/5/5*8 => 0
>ruby quiz148.rb "3 5 5 8 / / /"
3 5 5 / 8 * / => 3/(5/5*8) => 0

All the results are zero because it is using ruby's Fixnum#/. The last form
isn't quite optimal because it preferring to minimize divisions over
minimizing groupings/stack-depth. If you use the commented code in
Product#rdiv like this:

def rdiv(other)
# could do this to reduce grouping and stack depth
# but this will increase expensive divisions

@left.rdiv(other).div(@right) # might have more divisions now
#other.div(Group.new(self))
end


you'll get this:

>ruby quiz148.rb "3 5 5 8 / / /"
3 5 / 5 * 8 / => 3/5*5/8 => 0

Christian von Kleist

unread,
Dec 3, 2007, 12:14:06 PM12/3/07
to


Wow, there was a lot of activity on this quiz over the weekend!

Fine, here is my 5-minute solution which doesn't optimize parentheses:

#!/usr/bin/ruby
terms = []
ARGV[0].split(/\s/).each { |t| terms << (%w(+ - / *).include?(t) ?
"(#{terms.slice!(-2)} #{t} #{terms.slice!(-1)})" : t) }
puts terms[0]

It was originally a few lines longer as the ternary operator was an
if/else/end. Some of the solutions look very long! I haven't thought
about it yet, but the reduction of parentheses must be challenging!

Christian von Kleist

unread,
Dec 3, 2007, 12:17:54 PM12/3/07
to

Sorry, here's the non-obfuscated version:

terms = []
ARGV[0].split(/\s/).each do |t|
terms <<
if %w(+ - / *).include?(t)


"(#{terms.slice!(-2)} #{t} #{terms.slice!(-1)})"

else
t
end
end
puts terms[0]

Artem Voroztsov

unread,
Dec 3, 2007, 2:51:36 PM12/3/07
to
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

Eric DUMINIL

unread,
Dec 3, 2007, 3:44:22 PM12/3/07
to
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

Christian von Kleist

unread,
Dec 3, 2007, 3:56:27 PM12/3/07
to

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

Artem Voroztsov

unread,
Dec 3, 2007, 3:59:32 PM12/3/07
to
2007/12/3, Artem Voroztsov <artem.v...@gmail.com>:>

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

Errr .. "commutativity/non-commutativity" ->
"associativity/non-associativity"

Artem

Alpha Chen

unread,
Dec 3, 2007, 4:24:14 PM12/3/07
to
A pretty simplistic regex-based solution that doesn't minimize
parentheses:

#!/usr/bin/env ruby

str = ARGV[0].split(/[^.\d+\-*\/]/).join(' ')

while str !~ /^\(.*\)$/
str.sub!(/([^ ]+) ([^ ]+) ([+\-*\/])/, '(\1\3\2)')
end

puts str.gsub(/([+\-*\/])/, ' \1 ').sub(/^\((.*)\)$/, '\1')

Adam Shelly

unread,
Dec 3, 2007, 4:32:43 PM12/3/07
to
On 12/2/07, Daniel Martin <mar...@snowplow.org> wrote:

> "Adam Shelly" <adam....@gmail.com> writes:
>
> > This line:
> >> tok = Token.new(phrase,term.precedence)
> > should read
> > term = Term.new(phrase, term.precedence)
>
> Even with that cleanup, it doesn't seem to work:
>
> esau:/tmp$ ruby asrq148.rb '2 3 - 5 +'
> +
> esau:/tmp$ ruby asrq148.rb '2 3 -'
> -

I think you only changed 'Token' to Term, and missed the 'tok'=>'term' sub.
That's the only way I get what you show.

Anyway, Here's an updated version wihch handles the associativity test cases.

#postfix to infix
# ruby quix #148
# Adam Shelly

# v2


# Converts postfix to infix notation
# uses ruby's operators & precedence rules

$Operators = {
'&&'=>'0 ', '||'=>'0 ',
'=='=>'1 ', '==='=>'1 ', '<=>'=>'1 ',
'<='=>'2 ', '>='=>'2 ', '<' =>'2 ', '>'=>'2 ',
'^' =>'3 ', '|' =>'3 ',
'&' =>'4 ',
'<<'=>'5L', '>>'=>'5L',
'+' =>'6 ', '-' =>'6L',
'*' =>'7 ', '/' =>'7L', '%'=> '7L',
'**'=>'8R',
:term=>'10 '
}

class Term
attr_reader :precedence, :dir


def initialize str, groupPrec=nil
@s = str
@precedence = $Operators[str]||groupPrec||$Operators[:term]

@dir = @precedence[-1].chr
@precedence = @precedence.to_i
end
def isOp
@precedence != $Operators[:term].to_i


end
def parenthesize
@s="(#{@s})"
end
def to_s
@s
end
end

class Infix
def initialize rpn
stack=[]
rpn.split.each do |t|
term = Term.new(t)
if term.isOp

rval = stack.pop
lval = stack.pop


raise "Empty Stack" unless lval && rval

lval.parenthesize if lval.precedence < term.precedence or
term.dir=='R'&& lval.precedence == term.precedence
rval.parenthesize if rval.precedence < term.precedence or
term.dir=='L'&& rval.precedence == term.precedence
phrase = "#{lval} #{term} #{rval}"
term = Term.new(phrase,term.precedence)
#p term


end
stack.push term
end
@expr = stack.pop
raise "Extra terms" unless stack.size==0
end
def to_s

@expr.to_s
end
end

def test
puts Infix.new('2 3 +').to_s == '2 + 3'
puts Infix.new('56 34 213.7 + * 678 -').to_s == '56 * (34 + 213.7) - 678'
puts Infix.new('1 56 35 + 16 9 - / +').to_s == '1 + (56 + 35) / (16 - 9)'
puts Infix.new('1 2 + 3 4 + +').to_s == '1 + 2 + 3 + 4'
puts Infix.new('1 2 - 3 4 - -') .to_s == '1 - 2 - (3 - 4)'
puts Infix.new('2 2 ** 2 **').to_s == '(2 ** 2) ** 2'
puts Infix.new('2 2 2 ** **').to_s == '2 ** 2 ** 2'
puts Infix.new('1 2 3 4 5 + + + +').to_s == '1 + 2 + 3 + 4 + 5'
puts Infix.new('1 2 3 4 5 / / - -').to_s == '1 - (2 - 3 / (4 / 5))'
puts Infix.new('3 5 * 5 8 * /').to_s == '3 * 5 / (5 * 8)'
puts Infix.new('3 5 + 5 8 + -').to_s == '3 + 5 - (5 + 8)'
puts Infix.new('a b == c 1 + 2 < &&').to_s == 'a == b && c + 1 < 2'
puts Infix.new('1 2 << 4 <<').to_s == '1 << 2 << 4'
puts Infix.new('1 2 4 << <<').to_s == '1 << (2 << 4)'
end

if __FILE__ == $0
if ARGV.empty?
test
else
puts Infix.new(ARGV.join(' ')).to_s
end
end

James Koppel

unread,
Dec 3, 2007, 6:18:46 PM12/3/07
to
My solution failed on "3 5 * 5 8 * /," but I think I found a few changes that fixed that. Here's more new solution:

PREC = {:+ => 0,:- => 0,:* => 1,:/ => 1,:% => 1,:^ => 2}
LEFT_ASSOCS = {:+ => true,:- => true,:* => true,:/ => true,
:% => true,:^ => false}
RIGHT_ASSOCS = {:+ => true,:- => false,:* => true,:/ => false,
:% => false,:^ => true}

class TreeNode
attr_accessor :el,:left,:right
def initialize(el,left,right)
@el,@left,@right=el,left,right
end

def TreeNode.from_postfix(exp_arr)
stack = []
exp_arr.each do |exp_str|
if PREC.keys.include? exp_str.to_sym
op2,op1 = stack.pop,stack.pop
stack.push(TreeNode.new(exp_str.to_sym,op1,op2))
else
stack.push(exp_str.to_f)
end
end
stack.first
end

def to_minparen_infix
l,r = [left,right].map{|o|o.to_minparen_infix}
l = "(#{l})" if left.respond_to?(:el) and (PREC[left.el]<PREC[self.el] or
(PREC[self.el]==PREC[left.el] and not LEFT_ASSOCS[self.el])) #change in this line
r= "(#{r})" if right.respond_to?(:el) and (PREC[right.el]<PREC[self.el] or
(PREC[self.el]==PREC[right.el] and not RIGHT_ASSOCS[self.el])) #change in this line
l+" #{self.el} "+r
end
end

class Float
def to_minparen_infix
if self%1==0
to_i.to_s
else
to_s
end
end
end

puts TreeNode.from_postfix(ARGV.first.split(/ /)).to_minparen_infix

http://www.rubyquiz.com/

3. Enjoy!

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

2 3 5 + *

You can compare the results of these equations using the Unix utilities

For an added bonus, try to keep the parentheses added to infix
expressions to


the minimum of what is needed. For example, prefer these results:

$ ruby postfix_to_infix.rb '56 34 213.7 + * 678 -'


56 * (34 + 213.7) - 678

$ ruby postfix_to_infix.rb '1 56 35 + 16 9 - / +'


1 + (56 + 35) / (16 - 9)

to these:

$ ruby postfix_to_infix.rb '56 34 213.7 + * 678 -'
((56 * (34 + 213.7)) - 678)
$ ruby postfix_to_infix.rb '1 56 35 + 16 9 - / +'
(1 + ((56 + 35) / (16 - 9)))

Posting equations and your output is not a spoiler.


____________________________________________________________________________________
Be a better pen pal.
Text or chat with friends inside Yahoo! Mail. See how. http://overview.mail.yahoo.com/


Sharon Phillips

unread,
Dec 4, 2007, 7:58:01 AM12/4/07
to
Hi,

Didn't have much time for this, so here's a partial solution.
What I haven't done is work on ARGV and the bracket removal code is
pretty basic
produces
1+(56+35)/(16-9) - good
1+(2+(3+4)) - needs work...

Cheers,
Dave


eqn= %w[1 56 35 + 16 9 - / +]
ops= %w[+ - * /]

stack= []

eqn.each do |e|
if ops.include? e
b= stack.pop || 0
a= stack.pop || 0
if stack.empty?
stack= [a, e.to_sym, b]
else
stack << [a, e.to_sym, b]
end
else
stack << e
end
end

def disp item, depth
str=''
if item.class== Array
inner= item.inject('') {|sum, e| sum << (disp e, depth+1)}
inner= "(#{inner})" unless ([:*, :/].include? item[1]) || depth==0
str << inner
else
str << item.to_s
end
str
end

puts disp(stack,0)


Alex Shulgin

unread,
Dec 7, 2007, 3:44:51 PM12/7/07
to
On Dec 2, 9:16 pm, Daniel Martin <mar...@snowplow.org> wrote:
>
> # Data structures? We don't need no stinkin' data structures.
> # Postfix expression to infix expression via regular expressions.

Hi,

as no one have replied to this so far, I think I must express my
admiration to your solution. Bravo Daniel! :-)


--
Alex Shulgin

Reply all
Reply to author
Forward
0 new messages