in reply to Passing Variables

I refactored you code. Please study and ask questions:
################################################### # # # 2/24/21 # # Program takes finds the average in an # # array and lists out the numbers that were # # above the average found. # # # ################################################### ### Limitations - added 3/1/2021 ### This code will throw a fatal "Illegal division by zero" ### error if @numbers doesn't contain at least one element. my @numbers = qw(4 12 18 21 35); my $average = find_average(@numbers); print "The average found for this list is: $average \n" ; my @above_avg = above_average($average, @numbers); print "The numbers that were found above average are: @above_avg \n +"; sub find_average { my @nums = @_; my $sum; foreach my $num (@nums) { $sum += $num; } my $avg = $sum / @nums; return $avg; } sub above_average { my ($average_num, @nums) = @_; my @final_list; foreach my $num (@nums) { if ($num > $average_num) { push @final_list, $num } } return @final_list; } __END__ PRINTS: The average found for this list is: 18 The numbers that were found above average are: 21 35

Replies are listed 'Best First'.
Re^2: Passing Variables
by jdporter (Paladin) on Feb 25, 2021 at 22:40 UTC

    sub find_average needs to:

    1. initialize my $sum = 0;
    2. handle the arg list being empty. You probably don't want to throw a divide-by-zero exception.

    I suppose I'd write sub above_average like this:

    sub above_average { my $average_num = shift; grep { $_ > $average_num } @_ }
      Your comments are appropriate, thanks!
      My goal was to refactor the OP's code in a straight-forward way, with the intent of being instructive - there are of course imperfections .
      When coding this myself, I would be thinking about functions in List::Util and passing references to array instead of an array itself. But that is not what I thought my job here was. My intent was to present easy to understand code. But even so, I figure that the OP will have some questions about it.

        Example code that skips error handling, unless that is made clear in the accompanying text, is an example of how to do something badly. I'm sure you didn't intend it like that, but error handling and considering edge cases are the key to writing robust reliable code.

        Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
      "initialize my $sum = 0;"

      An interesting variation, that is probably premature optimization in simple cases, is to:

      sub average { my $sum = shift @_; ...;

      For algorithms such as moving averages or integrators initializing the accumulator with the first sample value can be essential to avoiding large glitches at the start of the result data.

      Optimising for fewest key strokes only makes sense transmitting to Pluto or beyond
Re^2: Passing Variables
by catfish1116 (Beadle) on Mar 01, 2021 at 20:11 UTC

    I get most of the revisions, except this line my ($average_num, @nums) = @_ Does this line of code say that the first arg is the number (average) and the second argument is the array? And that we are loading both of them into the default, (@_) array ? Marshall, thanks for your assistance with this. The Catfish

      You are close. All arguments to a subroutine are passed as a single ordered array. That array is accessed in the subroutine by @_.
       my ($average_num, @nums) = @_; means take the first element of @_ and make a copy of it into the subroutine's variable $average_num, then copy all of the elements that are "left over" to the subroutine's array of @nums.

      my ($average_num, $first_num, @rest_of_nums) = @_; is possible. But you can't have my ($average_num, @nums, $last_num) = @_; because @nums will consume all entries that are left in @_ and $last_num will be undefined.

      The @_ array is very special and some tricks can be played in certain circumstances. None of which apply in 99.9% of Perl code.

      Consider the following:

      use strict; use warnings; my @numbers = qw(4 12 18 21 35); my $average = find_average(\@numbers); print "The average found for this list is: $average \n" ; ## find_average will cause a fatal error if ## there are no numbers in the array. sub find_average { # subroutine gets a sinle value which is a reference # to an array, not an arry itself. This is much, much # faster if say the array has say 1,000 elements. my $num_array_ref = shift; # could be: my ($num_array_ref) = @_; # The shift operation is very slightly faster if # only one value is involved. my $sum; # no need to set $sum=0; # but I wouldn't quibble if you did that. foreach my $num (@$num_array_ref) { $sum += $num; } my $avg = $sum / @$num_array_ref; return $avg; } __END__ The average found for this list is: 18
      Update: I know that some Monks would say that the "return $avg" statement is extraneous because by default, Perl will return the value of the last statement in the subroutine. I don't want to relitigate that except to say that an explicit return statement is in my opinion good practice.