# Julia RPN calculator algorithm ported from Python version

I've just finished the Reverse Polish Notation task in Julia from rosettacode.com by porting the existing Python code.

There are only a few lines I don't get how to port:

maxcolwidths = [max(len(y) for y in x) for x in zip(*rp)]


and the subsequent lines that use maxcolwidths.

I managed to pretty print the test output anyway, but I'd really love to hear any opinions from you.

ops = [
:^ => :op_pow,
:* => :op_mul,
:/ => :op_div,
:- => :op_sub
]

for (op, func) in ops
@eval function ($func)(stack) b = pop!(stack) a = pop!(stack) push!(stack, ($op)(a, b))
end
end

input(prompt::String="") = (print(prompt); inp = chomp(readline()))
get_input(inp::String=input("Expression: ")) = (tokens = split(inp, ' '))

function rpn_calc{T<:String}(tokens::Vector{T}=get_input())
stack = {}
table = ["TOKEN" "ACTION" "STACK"]
for token in tokens
sym = symbol(token)
if sym in keys(ops)
action = "Apply op to top of stack."
@eval $(ops[sym])($stack)
else
action = "Push num onto top of stack."
push!(stack, parse(token))
end
table = [table; token action  join([string(s) for s in stack], ' ')]
end
return table
end

function test_rpn(rpn::String="3 4 2 * 1 5 - 2 3 ^ ^ / +")
println("For RPN expression: $rpn") table = rpn_calc(get_input(rpn)) i = 1 n_rows = length(table[:, 1]) while i <= n_rows if i == 1 println(join(table[i, 1:2], '\t'), "\t"^4, table[i, 3]) else println(join(table[i, :], '\t')) end i += 1 end println("\nThe final output value is:$(table[end, 3])")
end

test_rpn()


Output:

For RPN expression: 3 4 2 * 1 5 - 2 3 ^ ^ / +
TOKEN   ACTION                           STACK
3       Push num onto top of stack       3
4       Push num onto top of stack       3 4
2       Push num onto top of stack       3 4 2
*       Apply op to top of stack         3 8
1       Push num onto top of stack       3 8 1
5       Push num onto top of stack       3 8 1 5
-       Apply op to top of stack         3 8 -4
2       Push num onto top of stack       3 8 -4 2
3       Push num onto top of stack       3 8 -4 2 3
^       Apply op to top of stack         3 8 -4 8
^       Apply op to top of stack         3 8 65536
/       Apply op to top of stack         3 0.0001220703125
+       Apply op to top of stack         3.0001220703125

The final output value is: 3.0001220703125


In case you don't already know this ...

Here is what the line [max(len(y) for y in x) for x in zip(*rp)] is doing.

Say that

rp = [('a', 'b', 'c', 'd'), ('aa', 'bbb', 'cccc', 'ddddd')]
unzipped_rp = zip(*rp) # [('a', 'aa'), ('b', 'bbb'), ('c', 'cccc'), ('d', 'ddddd')]


This code does almost the same as what the line above does ...

max_lengths = []

for x in unzipped_rp:
lengths = []
for y in x:
lengths.append(len(y))
max_lengths.append(max(lengths))

print max_lengths


Output: [2, 3, 4, 5]

• Dec 15 '14 at 16:55

In Julia 0.4, the ops initialization causes a warning: WARNING: deprecated syntax "[a=>b, ...]" See below for preferred syntax. I don't remember if it works for Julia 0.3.

The use of @eval twice, once to create functions and once to call them, seems circuitous. A dictionary that maps symbols to functions would be more direct. E.g.:

ops = Dict(
:^ => (x,y)->x^y,
:* => (x,y)->x*y,
:/ => (x,y)->x/y,
:+ => (x,y)->x+y,
:- => (x,y)->x-y
)


The while loop could be written more compactly as for i=1:nrows.

• What I wnated to do was to build the functions programmatically to avoid redundancy. One has to use the @compat macro in order to make the Dict(:+ => "plus") syntax work in version 0.3. Dec 21 '14 at 5:27