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

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)'
        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)'
    else
        puts Quiz148.run(ARGV)
    end
end