[SPOILER] Fast, ugly, iterative solution to QOTW #23
Daniel Martin <martin-+m399P62/[email protected]>
| Newsgroups | gmane.comp.lang.perl.qotw.discuss |
|---|---|
| Message-ID | <[email protected]> |
This still isn't as fast as Rod Adams's solution, but it's the best I
could get. It's based on this idea:
What if I just had to write a program that printed out the answer
for n=4? What would be the fastest way to do that that might be
generalized for other n values easily? Figure out a way to write
perl code that can write the fast code, and feed that to eval().
The answer I came up with is something like this:
use vars qw(@parens);
@parens = map { '(' . ( ')' x $_ ); } (0..4);
my @a = ("x") x 4;
push @a, "\n";
my ( $i1, $t1, $u1 );
my ( $i2, $t2, $u2 );
my ( $i3, $t3, $u3 );
$u1 = 1;
for ( $i1 = 0 ; $i1 <= $u1 ; $i1++ ) {
$t1 = $t0 + $i1;
$a[0] = $parens[$i1];
$u2 = 2 - $t1;
for ( $i2 = 0 ; $i2 <= $u2 ; $i2++ ) {
$t2 = $t1 + $i2;
$a[1] = $parens[$i2];
$u3 = 3 - $t2;
for ( $i3 = 0 ; $i3 <= $u3 ; $i3++ ) {
$t3 = $t2 + $i3;
$a[2] = $parens[$i3];
$a[3] = $parens[ 4 - $t3 ];
print @a;
}
}
}
That is, have several nested for loops which counted the number of
closing parentheses that occur immediately following the first,
second, and third open parentheses, and inside the innermost loop add
the final open parenthesis and the necessary number of closing
parentheses, and print. (When it gets to the "print" line, each
element of @a contains a string of an open paren followed by some
number of close parens)
Anyway, I wound up with this after experimenting with several
variations on this same pattern - among other things, changing @a to
$a and using substr doesn't help - and of course my code doesn't
generate text that looks this nice. (One of the reasons
whitespace-as-syntax is painful: it makes building stuff like this
much more difficult in python than in perl)
It currently takes just under 1 minute to handle n=15. Unlike my
other solution, it is very friendly on memory.
#!/usr/bin/perl
use warnings;
use strict;
use vars '@parens';
sub for_loop_text ($$);
sub for_loop_text ($$) {
my ($n, $l) = @_;
my $p = $l - 1;
if ($l == $n) { return
join("\n",
"\$a[$p] = \$parens[$n - \$t$p];",
"print \@a;"); }
return join("\n",
"\$u$l=$l-\$t$p;",
"for (\$i$l=0;\$i$l<=\$u$l;\$i$l++) {",
" \$t$l = \$t$p + \$i$l;",
" \$a[$p] = \$parens[\$i$l];",
for_loop_text($n,$l+1),
"}");
}
my $n = shift;
die "Need a single argument" unless defined($n) and !@ARGV;
die "Very Funny" if ($n < 0);
if ($n==0) {print "\n";}
else {
@parens = map { '(' . ( ')' x $_ ); } (0..$n);
eval('my @a=("x") x '.$n.'; push @a, "\n"; ' .
'my $t0=0; ' .
join("\n", map {"my (\$i$_,\$t$_, \$u$_);"} (1..$n-1)) .
for_loop_text($n,1));
}
__END__