On Tue, Jun 03, 2003 at 08:36:05PM +0200 Kevin Pfeiffer wrote:
> Since it seemed like a nice exercise to work on I played with this some
> myself. Goals being to try to avoid global variables, use subroutines and
> keep MAIN 'uncluttered' and pass arguments to subs as needed.
>
> I think I did okay (holding breath), but I'm wondering about things like:
>
> my @sorted_keys = sort_results(\%score, \%opt);
>
> ## [...]
>
> sub sort_results {
>
> my %scores = %{ (shift) };
> my %opt = %{ (shift) };
>
> ## [...]
>
> }
>
> In the subroutine I'm dereferencing the hash and array and placing the
> values into new variables, correct? Should I instead be working directly on
> the original values (via a reference)? Would that be something like:
>
> sub sort_results {
>
> my ($score_ref, $opt_ref) = @_;
>
> ## etc...
> }
If you from then on referred to elements of the hash with something like
$score_ref->{ key };
then this would be it. It depends on what you want. By dereferencing the
whole data-structure you're essentially creating a copy. The downside of
that is that it needs more memory and is slightly less efficient.
However, that way your subroutine wont have side-effects. If you
directly work on the references, you'd change the hashes in your main
program (only on read-access, of course). That itself can be a valid
design decision. So it's up to you what you want.
> (Thanks for your always helpful advice and comments!)
>
> -K
>
>
> #!/usr/bin/perl
> use warnings;
> use strict;
>
> # from perl.beg (Stuart White)
>
> # desired result:
> # (attempted shots)
> #
> # Marbury 1
> # Jackson 3
> # Hardaway 1
> # Stoudemire 2
> # Bowen 3
> # Duncan 2
> # Williams 1
> # Marion 2
> # Ginobili 1
>
> {
>
> use Getopt::Std;
> my (%opt, $total_attempts);
>
> getopts('hl', \%opt); # commandline options
> my %score = get_data();
> my @sorted_keys = sort_results(\%score, \%opt);
>
> print (format_output([EMAIL PROTECTED], \%score));
>
> }
>
> ## END MAIN ##
>
> ## BEGIN SUBS ##
>
>
> sub get_data {
>
> my %scores;
>
> while (<DATA>) {
>
> if (/.*\] (\w+).*/) { # get player's name
> $scores{$1}++;
> }
>
> }
>
> return %scores;
>
> }
>
>
> sub sort_results {
>
> my %scores = %{ (shift) };
> my %opt = %{ (shift) };
>
> unless (%opt) { # default is sort by name
>
> my @sorted_keys = sort keys %scores ; # sorted by key
Was this branch ever executed so far? You create a lexical variable
inside...of course, @sorted_key wont be visible outside this
unless-block.
> } else {
>
> my $sort_hi if ($opt{h});
What is this statement for?
> my @keys_sorted_by_value = sort {
>
> compare ($scores{$a}, $scores{$b}, $opt{h})
>
> } keys %scores; # sorted by value
> }
>
> }
Ah, I should have read on. Either the unless or the else branch is the
last thing in your subroutine and so the last statement they evaluate is
returned. Hmmh, no. I wouldn't do it like that. It requires too much of
brain-power. ;-) What I often do in such a case is return early from a
sub and thus doing away with an else block. Like this:
sub sort_results {
my %scores = %{ (shift) };
my %opt = %{ (shift) };
if (%opt) {
return sort {
compare($scores{ $a }, $scores{ $b }, $opt{ h })
} keys %scores;
}
return sort keys %scores;
}
First of all, I turned the logic around. What previously was the else
part is now the if part. I use an explicit return there. So the second
return statement is only reached when %opt was false.
> sub compare { # sorts high & low scores
>
> my ($aa, $bb, $sort_hi) = @_;
> ($aa, $bb) = ($bb, $aa) if $sort_hi; # reverse if sort high
> (option h)
>
> return $aa <=> $bb;
>
> }
>
> sub format_output {
>
> my @sorted_keys = @{ (shift) };
> my %score = %{ (shift) };
> my $total_attempts;
> my $output = "\n";
>
> foreach (@sorted_keys) {
>
> $output .= sprintf "%12s %2s\n", $_, $score{$_};
> $total_attempts += $score{$_};
>
> }
>
> $output .= "================\n";
> $output .= "TOTALS: $total_attempts\n\n";
>
> }
The rest looks fine to me. However - and so turning back to your
original question - for this program you are probably best off by
directly working with the references. sort_results() would then look
like:
sub sort_results {
my ($scores, $opt) = @_;
if (%$opt) {
return sort {
compare($scores->{$a}, $scores->{$b}, $opt->{h})
} keys %$scores;
return sort keys %$scores;
}
By not creating a copy of your hashes, you save a little on memory and
CPU-cycles.
Tassilo
--
$_=q#",}])!JAPH!qq(tsuJ[{@"tnirp}3..0}_$;//::niam/s~=)]3[))_$-3(rellac(=_$({
pam{rekcahbus})(rekcah{lrePbus})(lreP{rehtonabus})!JAPH!qq(rehtona{tsuJbus#;
$_=reverse,s+(?<=sub).+q#q!'"qq.\t$&."'!#+sexisexiixesixeseg;y~\n~~dddd;eval
--
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]