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]

Reply via email to