[NBLUG/talk] I hate perl. :-)
Andru Luvisi
luvisi at andru.sonoma.edu
Thu Jun 8 17:55:37 PDT 2006
On Mon, 29 May 2006, William Tracy wrote:
[snip]
> I *know* that there have to be a few Perl nuts here. ;-) Given the
> nature of Perl nuts, I expect that someone will want to one-up my
> program (who can write it as a one-liner?). So, here it is. Have at
> it.
[snip]
Here's a short version for fun. I'm sure those better than me could make
it shorter.
#!/usr/bin/env perl
$"='';$b=[split(//,$ARGV[0])];r($b,0);sub r{my($s,$n)=@_;$n==$#$s?print
"@$s\n":map{r($s,$n+1);@$s[$n..$#$s]=@$s[$#$s,$n..$#$s-1];}(0)x(@$s-$n);}
With respect to your code, I'll offer some thoughts on style. These are,
of course, highly personal. YMMV.
My point isn't that my style is best. What I'll try to illustrate is how
I have found a style that reduces the distance between the mental models
that I use, and what's on the screen. That's one of the things that
TMTOWTDI is about. You learn to do some things in a Perlish way, and Perl
learns (with your help) how to do some things in a Youish way.
I notice that you already use the One True Brace Style. I like this style
because it fits more lines of code on the screen at once, and lets me see
more of the program at once.
I like to "use strict". It saves me from lots of typos. "use warnings"
has saved me from some bugs in the past too.
It's friendly for an error message to say what the problem is. A common
convention for command line utilities is to print out a "usage" line if no
arguments are given.
I like to add an exit at the end of the main program so that it is obvious
that "this is where the top level stuff ends and everything below here is
subroutines."
You usually don't want function prototypes, and you don't need one in this
case. The line that declares addAnagrams shouldn't have the () after
addAnagrams.
I prefer to receive arguments with:
my($var1, $var2) = @_;
I like the fact that it gives the variable list in a way that looks
similar to when the subroutine is called. This way when I'm trying to
remember things like "does it take the count or the name first?" I can see
what the arguments are, in order, at a glance, rather than having to look
through multiple lines and think "this is arg 2 which is the third arg and
comes after..."
When writing recursive functions, I prefer to list the base case first.
This mirrors the way that I reason about a recursive function, which is
through induction. I find it easier to read a function and think "It does
the right thing for $n == 0. Okay. ...and if it does the right thing for
$n-1, then it does the right thing for $n. Cool."
To me, the bit that removes the letter is tricky. When reading that
section, I have to figure out what's happening, and then figure out that
it's the correct thing to do. I would find that easier to read if it were
pulled out into a subroutine. Then while reading addAnagrams I just need
to convince myself that removing the character is the right thing to do,
and while reading removeChar, I just need to convince myself that it is
removing the character correctly. And since it's got a name, I don't need
to figure out what it's doing, only satisfy myself that it's doing it
correctly, which is much easier. The bit that grabs the letter and adds
it to $sofar is perfectly obvious to me, so I don't feel a need to name
that part. Of course, if both bits are obvious to you, there's no need to
name either one.
Well, there ya go. I hope it helps. Here's something like what I would
write in production code:
#!/usr/bin/env perl
use warnings;
use strict;
if (@ARGV != 1) {
print STDERR "Usage: $0 <word to find anagrams of>\n";
exit(1);
}
my $original = $ARGV[0];
print("Anagrams of ", $original, ":\n");
addAnagrams("", $original);
exit(0);
sub addAnagrams {
my($sofar, $left) = @_;
if(length($left) == 0) {
print $sofar, "\n";
} else {
for (my $counter = 0; $counter < length($left); ++$counter) {
addAnagrams($sofar . substr($left, $counter, 1),
removeChar($left, $counter));
}
}
}
sub removeChar {
my($s, $pos) = @_;
return substr($s, 0, $pos) . substr($s, $pos+1);
}
More information about the talk
mailing list