For example, your basic (and I mean basic, though not BASIC) adventure game is just:
%choices = ( "north" => \&go_north,
"south" => \&go_south,
"east" => \&go_east,
"west" => \&go_west,
"quit" => \&bail );
while (<>) {
chomp;
if (exists $choices{$_}) {
$choices{$_}(); # call the function
} else {
print "I'm sorry, I don't know how to: $_\n";
}
}
sub go_north {
print "You trudge northward.\n";
}
# etc for go_east, go_west, go_south
sub bail {
print "Goodbye!\n";
exit;
}
Those of you who took my Perl classes may remember that
$choices{s} = $choices{south};
$choices{widdershins} = $choices{east};
Or generate a lot of aliases automatically:
foreach my $cmd (keys %choices) {
$choices{substr($cmd, 0, 1)} = $choices{$cmd};
}
Try doing that with the "easy" approach:
while (<>) {
chomp;
if ($_ eq "north") {
print "You trudge northward.\n";
} elsif (...) {
...
} elsif ($_ eq "quit") {
print "Goodbye!\n";
exit;
} else {
print "I'm sorry, I don't understand: $_\n";
}
}
It's a real bugger to add aliases to that.
Callbacks:
sub apply_to_lines {
my ($filename, $code) = @_;
local $_;
open my $fh, "<", $filename or die "Can't open $filename: $!";
while (<$fh>) {
$code->($_);
}
close $fh;
}
$line_count = 0;
apply_to_lines( "/etc/passwd", sub { $line_count++ } );
print "/etc/passwd has $line_count lines\n";
$longest_line = '';
apply_to_lines( "/etc/passwd", sub {
$line = shift;
$longest_line = $line if length($line) > length($longest_line);
});
print "The longest line is: $longest_line\n";
The same subroutine, apply_to_lines, can be used to both count lines or find the longest line. That's the fundamental idea of callbacks: customizing your code by letting the user supply an action, not just a value or variable to store a value into.
Of course, you can do cooler stuff in Perl with code refs than you can in C with function pointers. For example, mess with the symbol table
foreach my $colour (qw(red green blue black nastybrown)) {
no strict 'refs';
*$colour = sub { print "<font color=$colour>@_</font>" };
}
*$colour means "the symbol table entry for the symbol whose name is in $colour". So the first time through the loop it's "the symbol table for the symbol red", then it's "the symbol table entry for green", etc. By assigning a code reference to that symbol table entry, you change what Perl thinks is the function called red, green, etc. This kind of messing with Perl's head is what Damian and Dominus do five times before breakfast.
End result is that we define a bunch of subroutines, each of which prints some different HTML. If you haven't thought of it, it's fun to realize that because $colour is a private (my) variable, it is allocated new memory each time through the loop and so each subroutine refers to a different value of $colour, whereas @_ is a global variable and so the automatically generated red, green etc. subroutines use whatever value @_ has when the subroutine is called. Once you've figured out what that meant, figure out what happens when you take my out of the foreach loop.
And so on. Lots more uses, no doubt explained in the Alcapacapal, but ENOSLEEP. Jenine's written her epistles to her friends ("First Coloradoans, Chapter 2, Verse 3: Raley didst try me verily, and I didst want to smite her. Vengeance will be mine, sayeth the Mommy, especially if you don't stop flipping those bloody light switches!") and it's bedtime. Ninight.
--Nat
gcc nested functions (Score:1)
added to the C99 standard....
A fun ML thing is partially applied functions: If foo takes two
args, a and b, then (foo a) is the same as (fun b -> foo a b).
Of course, to do this you need static typing.
Choices (Score:2)
--
xoa
Re:Choices (Score:2)
It would be much better to use the
Text::Abbrevmodule (it's part of the core). It'll create unique abbreviations. Of course the code would be a little more convoluted:(or something like that - completely untested and of my hat)
Re:Choices (Score:2)
Or, rather than filling the hash with small keys, make a smarter dispatcher:
Mmm, $cmd, @cmd and %cmd used in 3 lines... That's one point in the Perl purity test. :-)