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

Hey guys,

It's been a while. Both since I've used Perl and come here seeking knowledge and your kind help.

Since it's been a while, I decided I would "ride the Llama" once more. So far, I've just been cracking along and enjoying a nice refresher.

But unfortunately, I seem to have hit a snag in the first exercise on chapter 4. I've stared and stared at my code...and stared some more. When I was done staring, I stared one more time.

Now. I've decided to cave and ask for help. I just don't know why my 'total' function is behaving so oddly. Here's the proglet:

----------------------------------------------------------

#!/usr/bin/perl use strict; sub total { my $number; foreach (@_) { $number += @_[$_]; } return $number; } my @fred = qw{ 1 3 5 7 9 }; my $fred_total = &total(@fred); print "The total of \@fred is $fred_total.\n"; print "Enter some numbers on separate lines: \n"; my $user_total = &total(<STDIN>); print "The total of those numbers is $user_total.\n\n";
------------------------------------------------------
Any input would be appreciated!

Danke!

2006-10-01 Retitled by g0n, as per Monastery guidelines
Original title: '0dd llama'

Replies are listed 'Best First'.
Re: Having problems with addition in sub
by atcroft (Abbot) on Oct 01, 2006 at 04:18 UTC

    grep is right-the problem is in the foreach in your total function. By saying @_[$_] inside your loop, you are using values from the array passed in (@fred) to pick the items to add.

    1. At line 12 of your code, @fred contains the values ( 1, 3, 5, 7, 9 ).
    2. At line 13, you call your total function. At line 5 (just inside the total function), @_ contains the content of @fred from the main body (i.e., ( 1, 3, 5, 7, 9 )).
    3. Now, when you begin the foreach loop, you are looping thru the values (1, 3, 5, 7, 9), but inside the loop $_ is taking on the value you are currently working with, so on the first pass, $_ contains the value 1, so the value at index 1 of @_ (i.e., 3), is added to $number, giving it the value 3.
    4. Second pass, $_ contains 3, so the value in $_ is 7, and $number thus becomes 10.
    5. On pass three, $_ contains 5, which is undefined in @_ (a warning would be generated were you using either '-w' or 'use warnings;'), so $number does not change.
    6. The same thing occurs for passes four and five, where $_ becomes 7 and 9, respectively.
    7. Thus, $number contains 10 when returned from the first call to &total.

    A similar situation will occur if the user provides numbers that exceed the number of values they provide.

    HTH.

      A nit (which I'm sure atcroft knows about) for the benefit of OP: it is important to remember from time to time that $_ is actually an alias to each element in the list being processed by a for loop (or map or grep). This is important because if you change $_ inside the loop the current list element's value is altered. Consider:

      use strict; use warnings; my @array = qw(1 3 5 6); ++$_ for @array; print "@array";

      Prints:

      2 4 6 7

      Each of the elements in the array @array has been incremented by the for loop.


      DWIM is Perl's answer to Gödel
Re: Having problems with addition in sub
by grep (Monsignor) on Oct 01, 2006 at 03:48 UTC
    You have two a syntax mistake s in your foreach .
    UPDATE: ysth is correct about the chomp. It was uneeded as my code was orginally posted
    sub total { my $number; foreach (@_) { # you are allowing input from STDIN which is going to # add a \n on the end. So you're going to have to add # a chomp chomp; # UPDATE: ysth is correct, but I'll leave it in # for the next line I added next if (!/^[+-]?[[:digit:].]+$/); # UPDATE: This is slightly better # $number += @_[$_]; # $_ is assigned the value of the element in @_ not the index # So you want $number += $_; } return $number; } my @fred = qw{ 1 3 5 7 9 }; my $fred_total = &total(@fred); # & not required print "The total of \@fred is $fred_total.\n"; print "Enter some numbers on separate lines: \n"; my $user_total = &total(<STDIN>); print "The total of those numbers is $user_total.\n\n";


    grep
    Mynd you, mønk bites Kan be pretti nasti...
      Trailing (and leading, for that matter) whitespace is ignored (with no warning) when you use a string in numeric context, so there's no need for the chomp.
Re: Having problems with addition in sub
by ikegami (Patriarch) on Oct 01, 2006 at 16:51 UTC

    It has already been mentioned that
    foreach (@_) { $number += @_[$_]; }
    should be
    foreach (@_) { $number += $_; }
    If you wanted $_ to have the indexes, you'd use
    for (0..$#_) { $number += $_[$_]; }

    Another way is to use highly efficient sum from the core module List::Util.

    use List::Util qw( sum ); my @fred = qw{ 1 3 5 7 9 }; my $fred_total = sum @fred; my $line = <STDIN>; my @nums = split ' ', $line; my $user_total = sum @nums;
Re: Having problems with addition in sub
by ghoti (Acolyte) on Oct 01, 2006 at 17:17 UTC
    First, praise for using strict!

    ...and now couple more nits....
    1. By adding use warnings; perl would have told you some useful information.
    2. In total(), $number is not initialized. Sure you can ass-u-me that perl will set it to 0 but it will save you time and head ache later if you get in the habit of initializing all your variables (i.e., my $number = 0;)
Re: Having problems with addition in sub
by jbert (Priest) on Oct 02, 2006 at 12:58 UTC
    Obviously other posts have covered the main points:
    1. you meant $_, not @_[$_]
    2. even if you'd wanted to index the array, the syntax is $array[$index], not @array[$index]
    3. 'use warnings' (or adding -w to your shebang line) should be your first step in troubleshooting (actually the zeroth step, you should always pre-emptively turn it on).
    I'd just like to add that I think the problem also owes something to poor choice of variable names. I'm not one to say that using $_ and @_ is always evil, but compare how easy it is to spot the error:
    sub total { my @numbers = @_; my $total = 0; foreach my $value (@numbers) { $total += $numbers[$value]; # Wait, what? } return $total; }
Re: Having problems with addition in sub
by halley (Prior) on Oct 02, 2006 at 14:51 UTC
    Another tidbit: you don't need the ampersand to make a call. In fact, it can cause problems in some cases. Just call a sub by its name, offering the desired arguments.
    my $fred_total = total(@fred);

    [useless update: this is my 900th writeup]
    --
    [ e d @ h a l l e y . c c ]

Re: Having problems with addition in sub
by Anonymous Monk on Oct 02, 2006 at 08:19 UTC

     $number += @_[$_]; ==>this is the mistake.

    When you pass the array to the function, the default @_ will grab everything and store it.

    And when you use the foreach, $_ will loop through the entire array and will have the current value when you process it.

    Correct usage should be:

    ======================= $number += $_;

    Your proglet works fine after this.

    Hope you understood now :-)

    -Anand

    Code tags added by GrandFather