En op 08 september 2002 sprak Tina Mueller:
> the thing which is interesting for me with every new hole is
> the algorithm. (i have yet to understand eugene's one =)

The algorithm is nothing special. It's just hard to see what's
happening because of the compactness of my shortest solution.

The winning solution:
#!perl -p
sub
w{s/
([@_^+-])(.*)/ $2 $1/g}sub
f{s/\(([^()]+)\)/f($_=$+)/e?&f:w~w;$_}f
s/\s//g/s/\d+/$&
/g

Rewritten:

#!perl -p
sub w{
    s/\n([@_^+-])(.*)/ $2 $1/g
}
sub f{
    s/\(([^()]+)\)/$_=$+;&f/e ? &f : w(~w);
    $_
}
s/\s//g;
s/\d+/$&\n/g;
f

What happens is this:
First, all whitespace is removed. Then every operator is marked by a
newline. This newline indicates that this operator has not yet been put
into RPN:

1+2*3+4*(5+6)+7 => 1\n+2\n*3\n+4\n*(5\n+6\n)+7\n

The "\n" precedes each operator, but not always directly. When there are
parens, it's inside them. This is not a problem for my algorithm, since
it deletes the ()'s and leaves the \n's alone.

Now the parenthesised expressions are resolved, starting with the
innermost expressions. The logic is similar to that of

1 while s/\(([^()]+)\)/$_=$+;&f/e;

The result is:

1\n+2\n*3\n+4\n*5 6 +\n+7\n

The function w is called twice: once without arguments, and once with an
argument. w() without argument puts the multiplications (but only those
preceded by \n's) in the proper order:

1\n+2 3 *\n+4 5 6 + *\n+7\n

and w called with an argument treats the additions:

1 2 3 * + 4 5 6 + * + 7 +\n

Interestingly, this use of \n as a marker was not my idea at all, it
just haoppened. My idea was to use two markers, to indicate the parts of
the expression that were already in RPN. To start with, this would mean
only the numbers:

a1z + a2z * a3z + a4z * (a5z + a6z) + a7z

and then to remove those markers as soon as a part of the expression was
put into RPN:

=> a1z + a2z * a3z + a4z * a5 6 +z + a7z
=> a1z + a2 3 *z + a4 5 6 + *z + a7z
=> a1 2 3 * +z + a4 5 6 + *z + a7z
... => a1 2 3 * + 4 5 6 + * + 7 +z

Look at my 175.67 solution to see how that worked. This solution
contained the regexp m#z([*/])a(.*?)z# . Notice the non-greedy match,
which could also be written as a([^z])z. When I replaced the end marker
with a newline, I could just use (.*). And then I didn't need the -l
command switch. And it had a better tie-break score.

And later, I tried to remove the begin marker 'a', and it worked! This
was a big but not unwelcome suprise.

So my final solution used a different algorithm than my first, and that
happenedpurely by coincidence. I didn't even know how my solution
actually worked before I started writing this mail.

OK, this is probably more than you wanted to know, but "To write is to
delete, and this post wasn't worth the effort to delete", as a wise man
once said [1].

(-ugene

[1] Herman Finkers
-- 
The competent programmer is fully aware of the strictly limited size of
his own skull; therefore he approaches the programming task in full
humility, and among other things he avoids clever tricks like the
plague. -- (-dsger |)ijkstra

Reply via email to