Приглашаем посетить
Бестужев-Марлинский (bestuzhev-marlinskiy.lit-info.ru)

Section A.3.  Answers to Chapter 4 Exercises

Previous
Table of Contents
Next

A.3. Answers to Chapter 4 Exercises

  1. Here's one way to do it:

        sub total {
          my $sum;  # private variable
          foreach (@_) {
            $sum += $_;
          }
          $sum;
        }
    

    This subroutine uses $sum to keep a running total. At the start of the subroutine, $sum is undef since it's a new variable. Then, the foreach loop steps through the parameter list (from @_) using $_ as the control variable. (There's no automatic connection among @_, the parameter array, and $_, the default variable for the foreach loop.)

    The first time through the foreach loop, the first number (in $_) is added to $sum. $sum is undef since nothing has been stored there. Since we're using it as a number, which Perl sees because of the numeric operator +=, Perl acts as if it's already initialized to 0. Perl thus adds the first parameter to 0, and puts the total back into $sum.

    Next time through the loop, the next parameter is added to $sum, which is no longer undef. The sum is placed back into $sum and on through the rest of the parameters. Finally, the last line returns $sum to the caller.

    There's a potential bug in this subroutine depending on how you think of things. Suppose this subroutine was called with an empty parameter list as we considered with the rewritten subroutine &max in the chapter text. In that case, $sum would be undef which would be the return value. But in this subroutine, it would probably be more correct to return 0 as the sum of the empty list rather than undef. (If you wished to distinguish the sum of an empty list from the sum of, say, (3, -5, 2), returning undef would be the right thing to do.)

    If you don't want a possibly undefined return value, though, it's easy to remedy: simply initialize $sum to zero rather than using the default of undef:

        my $sum = 0;
    

    Now the subroutine will always return a number even if the parameter list were empty.

  2. Here's one way to do it:

        # Remember to include &total from previous exercise!
        print "The numbers from 1 to 1000 add up to ", &total(1..1000), ".\n";
    

    We can't call the subroutine from inside the double-quoted string,[*] so the subroutine call is another separate item being passed to print. The total should be 500500, a nice round number. And it shouldn't take any noticeable time at all to run this program; passing a parameter list of 1,000 values is an everyday task for Perl.

    [*] We can't do this without advanced trickiness. It's rare to find anything that you absolutely can't do in Perl.

  3. Here's one way to do it:

        sub average {
          if (@_ =  = 0) { return }
          my $count = @_;
          my $sum = &total(@_);               # from earlier exercise
          $sum/$count;
        }
        sub above_average {
          my $average = &average(@_);
          my @list;
          foreach $element (@_) {
            if ($element > $average) {
              push @list, $element;
            }
          }
          @list;
        }
    

    In average, we return without giving an explicit return value if the parameter list is empty. That gives the caller undef[*] to report that no average comes from an empty list. If the list wasn't empty, using &total makes it simple to calculate the average. We didn't need to use temporary variables for $sum and $count, but doing so makes the code easier to read.

    [*] Or an empty list, if &average is used in a list context.

    The second sub, above_average, builds and returns a list of the desired items. (Why is the control variable named $element instead of using Perl's favorite default, $_?) Note that this second sub uses a different technique for dealing with an empty parameter list.

    Previous
    Table of Contents
    Next