Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

returning array in custom subroutine

by dovah (Novice)
on Aug 31, 2016 at 13:26 UTC ( [id://1170881]=perlquestion: print w/replies, xml ) Need Help??

dovah has asked for the wisdom of the Perl Monks concerning the following question:

Hi monks. I am quite new to perl, except from some easy regex stuff :P so, today I was trying to setup a normalizing function, which would ideally work on arrays. The idea is that we have an input array whose items have to be normalized between 0 and 1. Normalization is quite simple, achieved by:  normalized_array_item = (array_item - array_min) / (array_max - array_min) So here is a draft of what I prepared.
sub normalizer { # to normalize an input array bw 0 and 1 use List::Util qw( min max ); # import min and max module my $min_numarray = min @array; # get min of args my $max_numarray = max @array; # get max of args foreach my $element(@array) { # loop over args my $numdiv = $element - $min_numarray; # numerator: n - min + total my $numden = $max_numarray - $min_numarray; # denominator: max + total - min total my $normalized = my $numdiv / $numden; # normalize } return $normalized; }
Actually I would like my function to return the whole normalized array. Could you help me with that? Thanks

Replies are listed 'Best First'.
Re: returnong array in custom subroutine
by stevieb (Canon) on Aug 31, 2016 at 13:51 UTC

    You've got a few issues with your code. You're re-declaring some of your variables with my incorrectly (always put use warnings; and use strict; at the top of your scripts), you're using @array in a global sense when you should be passing it into the subroutine instead, and unless you have good reason not to, you should be putting your use statements at the top of your code as it's far easier to see what the code is using.

    With these changes and additions, does it do what you expect? I create the array, send it in as a list of parameters to the sub, perform actions on each element, push the result to a new array, then when done the loop, return the entire new array:

    use warnings; use strict; use List::Util qw( min max ); my @not_normalized = qw(5 4 9 9 6); my @normalized = normalizer(@not_normalized); print "$_\n" for @normalized; sub normalizer { my @not_normalized = @_; my $min_numarray = min @array; my $max_numarray = max @array; my $normalized; my @normalized_list; foreach my $element (@not_normalized){ my $numdiv = $element - $min_numarray; my $numden = $max_numarray - $min_numarray; $normalized = $numdiv / $numden; push @normalized_list, $normalized; } return @normalized_list; }

    Output:

    0.2 0 1 1 0.4

    update: renamed arrays to have sensible names

      Yes, thanks! :) this is what I expected.
Re: returning array in custom subroutine
by AnomalousMonk (Archbishop) on Aug 31, 2016 at 17:13 UTC

    Other approaches using a for-loop are perfectly OK and you should be sure you understand them. But as you gain more experience with Perl, you'll see that there's a good deal of wasted motion there. Here's a solution I find neat and easy to understand — once you understand the powerful map built-in!

    c:\@Work\Perl\monks>perl -le "use warnings; use strict; ;; use List::Util qw( min max ); ;; my @not_normalized = qw(5 4 9 9 6); ;; my @normalized = normalizer(@not_normalized); ;; printf qq{$_ } for @normalized; ;; sub normalizer { my $min_numarray = min @_; my $max_numarray = max @_; my $numden = $max_numarray - $min_numarray; ;; return map { ($_ - $min_numarray) / $numden } @_; } " 0.2 0 1 1 0.4
    The next thing to understand is that if you are processing a large array (and what is "large" depends on your circumstances), it may be advantageous to pass (and perhaps return) the array by reference; see perlref and perlreftut.

    Update: And if you can stand to operate on the elements of the array in place (definitely fastest for large arrays), here's yet another approach:

    c:\@Work\Perl\monks>perl -le "use warnings; use strict; ;; use List::MoreUtils qw(minmax); ;; my @array = qw(5 4 9 9 6); ;; normalizer(@array); ;; printf qq{$_ } for @array; ;; sub normalizer { return unless @_ >= 2; my ($min, $max) = minmax @_; my $numden = $max - $min; ;; $_ = ($_ - $min) / $numden for @_; } " 0.2 0 1 1 0.4
    This depends on the aliasing of the  @_ function argument array. See perlglossary for a brief discussion of the concept of an alias. Also see aliasing in Wikipedia.


    Give a man a fish:  <%-{-{-{-<

      Thanks for the for loop suggestion! Will definitely try it :)
Re: returnong array in custom subroutine
by kroach (Pilgrim) on Aug 31, 2016 at 14:08 UTC
    There are two errors in the code:
    my $normalized = my $numdiv / $numden;
    You declare $numdiv again with my, resetting it's value, this should have produced a warning.

    foreach my $element(@array) { # loop over args ... my $normalized = my $numdiv / $numden; # normalize } return $normalized;
    You make a $normalized variable local to the loop and try to return it outside, this shouldn't compile with strict vars.

    Using warnings and strict will help you catch simple mistakes like these.

    You can use $element to modify the array (it is an alias, not a copy). $numden is not dependent on the array element, so there is no need to calculate it each time.

    This should work:
    use List::Util qw( min max ); sub normalizer { my @array = @_; my $min_numarray = min @array; my $max_numarray = max @array; my $numden = $max_numarray - $min_numarray; foreach my $element (@array) { my $numdiv = $element - $min_numarray; $element = $numdiv / $numden; } return @array; }
Re: returnong array in custom subroutine
by perldigious (Priest) on Aug 31, 2016 at 13:54 UTC

    use strict; use warnings; my @not_normalized = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); my @normalized = normalizer(@not_normalized); print "@normalized"; sub normalizer { # to normalize an input array bw 0 and 1 my @array = @_; use List::Util qw( min max ); # import min and max module my $min_numarray = min @array; # get min of args my $max_numarray = max @array; # get max of args my $numden = $max_numarray - $min_numarray; # denominator: max tot +al - min total foreach my $index (0..$#array) { # loop over args my $numdiv = $array[$index] - $min_numarray; # numerator: n + - min total $array[$index] = $numdiv / $numden; # normalize } return @array; }

    I love it when things get difficult; after all, difficult pays the mortgage. - Dr. Keith Whites
    I hate it when things get difficult, so I'll just sell my house and rent cheap instead. - perldigious

      While the code I give here is just yours with minimum modifications to do what you want, I actually first attempted to find a CPAN module with a "normalize this array of numbers" sort of function built in. I couldn't find one, but perhaps I'm just bad at searching, because it seems like that's something that probably exists...

      I love it when things get difficult; after all, difficult pays the mortgage. - Dr. Keith Whites
      I hate it when things get difficult, so I'll just sell my house and rent cheap instead. - perldigious

        When I tried "statistics normalize" this came right up.

        Celebrate Intellectual Diversity

        Thanks! Actually before (attempting to) write my own subroutine, I also tried to google that and I did not find any hits. Maybe we're both bad at googling. :)

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others scrutinizing the Monastery: (3)
As of 2024-04-25 13:05 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found