Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things
 
PerlMonks  

Duplicate Results from Subroutine

by zenrhino (Initiate)
on May 23, 2013 at 20:12 UTC ( #1035019=perlquestion: print w/ replies, xml ) Need Help??
zenrhino has asked for the wisdom of the Perl Monks concerning the following question:

Greetings, Monks.

I'm working my way through Learning Perl 6th Ed and doing an expansion of Probs. 1-3 on pp. 78.

My problem is that is I use the @abvlist array as seen on list 16, I get the correct answer. If I call the function directly as on line 17, I get a duplicate set of the answer. I am at a loss as to why this happens.

I suspect, that as with most things in Perl, it's going to be something simple and small I'm looking right over.

Run as is, I get the following:

The count of numbers in @numlist is 8.
The total of @numlist is 40.
The mean average of @numlist is 5.
The list of numbers in @numlist over the mean is 7,9.
The list of numbers that will probably be duplicates in @numlist over the mean is 7,9,7,9.
The standard deviation of numbers in @numlist is 2.
Finished in 0.1s

I get the same results from the commandline in powershell as I do from the build run in ST2.

I've searched for "duplicate results from subroutine" and "duplicate results from function" and didn't see anything.

All the best,

-ZR

#!/C:\Perl64\bin\perl.exe use strict; use warnings; my @numlist = qw(2 4 4 4 5 5 7 9); my $numlist_avg = avg(@numlist); my @cands; my @abvlist = abv(@numlist); my $sigma = std_dev(@numlist); my @squares; my $len = $#numlist + 1; print "The count of numbers in \@numlist is ", $#numlist + 1, "\.\n"; print "The total of \@numlist is ", total(@numlist), "\.\n"; print "The mean average of \@numlist is ", avg(@numlist), "\.\n"; print "The list of numbers in \@numlist over the mean is ", join(',', + @abvlist), "\.\n"; print "The list of numbers that will probably be duplicates in \@numli +st over the mean is ", join(',', abv(@numlist)), "\.\n"; print "The standard deviation of numbers in \@numlist is ", $sigma, "\ +.\n"; sub total { my $endresult = 0; foreach my $x (@numlist) { $endresult += $x; } return $endresult; } sub avg { my $len1 = $#numlist + 1; return total(@numlist) / $len1; } sub abv { foreach my $cand (@numlist) { if ($cand > $numlist_avg) { push(@cands, $cand); } } return @cands; } sub std_dev { my $len2 = $#numlist + 1; foreach my $num (@numlist) { my $step1 = $num - $numlist_avg; my $step2 = $step1 * $step1; push(@squares, $step2); } my $start2 = 0; foreach my $num2 (@squares) { $start2 += $num2; } return sqrt($start2 / $len2); }

Comment on Duplicate Results from Subroutine
Download Code
Re: Duplicate Results from Subroutine
by stephen (Priest) on May 23, 2013 at 21:08 UTC

    To fix this specific problem, you need to localize @cands in abv() rather than up at the top of the script.

    sub abv { # Move from top of the script down to here my @cands = (); foreach my $cand (@numlist) { if ($cand > $numlist_avg) { push(@cands, $cand); } } return @cands; }

    Each time you call abv(), it remembers @cands from the last time you call it, and returns the duplicate values as well as the new ones.

    It's a good idea to localize your variables to the smallest possible block. Declare them in the subroutine that's using them.

    You're also assuming that $numlist_avg has already been determined. If you tried calling abv() before setting $numlist_avg, you might wind up with a hard-to-trace error.

    While we're at it, you're passing @numlist to the subroutine as an argument, but not actually using that argument. If you called abv(1,2,3) it would still go over @numlist to get the result. So, instead, you should do:

    sub abv { my (@numlist_args) = @_; my @cands = (); my $avg = avg(@numlist_args); foreach my $cand (@numlist_args) { if ($cand > $avg) { push(@cands, $cand); } } return @cands; }
    Good luck!

    stephen

      Daaaang.

      That's like five lessons for the price of one.
      I did bang my head against ways to generalize the subroutines not using @numlist, but figured asking two questions at once would be selfish.

      So if I have this right, @_ holds the list of parameters passed to the abv subroutine, and we know it gets passed as a list because it ends up as my (@numlist_args) and not my @numlist_args which would pass a scalar. So then the foreach loop works on whatever is passed as paramaters.

      I took your help and also applied it to the std_dev subroutine which had the effect of letting me get rid of all of the hardcoded variables that I had originally and making the subroutines easier to read.

      Thank you very much!

      -Clint

        So if I have this right, @_ holds the list of parameters passed to the abv subroutine.
        You got it, here's a version that uses the special variables $_ and @_
        sub abv { my $avg = avg(@_); my @cands = (); foreach (@_) { ($_ > $avg) and push(@cands, $_); } return @cands; }
        I flicked the if statement into a logical and, for each iteration of the foreach $_ will be the same as $cand was in the original function.
Re: Duplicate Results from Subroutine
by dbuckhal (Monk) on May 24, 2013 at 06:03 UTC
    Looks like they got you taken care of, so I thought I would pass a little tip about finding array size
    sub avg { my $len1 = $#numlist + 1; return total(@numlist) / $len1; }
    You can get the array length by this:
    my $len1 = @numlist;
    ... which is the scalar value of your array, which is the size.

    Enjoy Learning Perl!

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1035019]
Approved by Paladin
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others studying the Monastery: (10)
As of 2014-10-21 10:51 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    For retirement, I am banking on:










    Results (100 votes), past polls