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

Hi Monks,

I've spent the last 2 hours on this one. I hope someone can help me. I believe I am close. I need a program that will add or multiply any number of values and print out result. The problem I'm having is with the "any number of values part." I'm ashamed to admit it, but yes this is homework.
# Program that will add or multiply as many numbers that are entered. # Use the program as such: program.pl add 3 2 1 use warnings; use strict; my $rtn; my $len = @ARGV; #Get the number of elements $len = $len - 1; if ($ARGV[0] =~ /add/) { $rtn = add($ARGV[1]..$ARGV[$len]); print "The sum is: $rtn"; } elsif ($ARGV[0] =~ /multiply/) { $rtn = multiply($ARGV[1]..$ARGV[$len]); print "The product is $rtn"; } sub add { my $sum = 0; my @data = @_; for (@data){ $sum += $_; } return $sum; } sub multiply { my $product = 1; my @data = @_; for (@data) { $product *= $_; } return $product; }
Thanks.

Replies are listed 'Best First'.
Re: Passing Any Range of Values to a Sub (slice)
by tye (Sage) on Jul 02, 2003 at 03:37 UTC

    If you run "script add 2 1 3 7 5" then your $ARGV[1]..$ARGV[$len] boils down to 2..5 which is the same as 2,3,4,5. You are close. You want 1..$len and use that to get items from @ARGV. That is called an "array slice" and is written @ARGV[1..$len] (note the @ not $).

                    - tye
      Of course, and array slice, I've used them before, but it never came to me when trying to figure out this problem. Thanks a million.
Re: Passing Any Range of Values to a Sub
by The Mad Hatter (Priest) on Jul 02, 2003 at 03:37 UTC

    Update TIMTOWTDI, as seen by tye's post below. All a matter of perspective. ; )

    In this case, you don't want to use the .. operator (which produces consecutive numbers), you just want to pass a list of values. @ARGV contains this list, except for the first element, which is the operation. So shift off the first element from @ARGV, figure out what to do, pass the rest of @ARGV to the appropriate function, and you should be set.

    I'd suggest you figure it out yourself, but here's the code if you really want to see it...

Re: Passing Any Range of Values to a Sub
by bobn (Chaplain) on Jul 02, 2003 at 05:05 UTC
    Congratulations on use of warnings and strict. You'll save much heartache down the road that way.

    my $op = shift @ARGV; # first element of @ARGV now in $op and removed from @ARGV. print add(@ARGV) . "\n" if $op eq 'add'; print multiply(@ARGV) . "\n" if $op eq 'multiply'; # subs as before
    Or you can get fancy and have a hash table of functions:
    my $op = shift @ARGV; # first element of ARGV in $op and removed from @ARGV. my %h = ( add => \&add, multiply => \&multiply); print $h{$op}->(@ARGV) . "\n"; # subs as before


    --Bob Niederman, http://bob-n.com
Re: Passing Any Range of Values to a Sub
by bart (Canon) on Jul 02, 2003 at 08:18 UTC
    Your problem has been solved several times over from what I see... But as for alternatives, may I point you towards reduce(), available in List::Util? Your multiply and add functions are typical examples to be implemented this way.
    use List::Util 'reduce'; my $op = shift @ARGV; if ($op =~ /add/) { my $sum= reduce { $a + $b } @ARGV; print "The sum is $sum\n"; } elsif ($op =~ /mult/) { my $product= reduce { $a * $b } @ARGV; print "The product is $product\n"; } else { print "Huh? I don't know how to '$op'?\n"; }
    Yes, that's all the code it needs.
Re: Passing Any Range of Values to a Sub
by Petras (Friar) on Jul 02, 2003 at 10:13 UTC
    I'm ashamed to admit it, but yes this is homework.

    Hey, at least you had the umption to admit it upfront. There are a lot of posts that stink of homework questions but start off with something like, "SO I was playing around with Perl and had an idea but I can't make it work...." which translates to, "This is due tomorrow. Help!!!"

    What makes this a good post is that
    1. You admit upfront that it's homework (show some integrity!)
    2. You did some work, and it looks like some research (not just "How do I do this thing?")
    Keep working at it. You'll pass me in no time (if you haven't already ;)

    Cheers!
    Petras
    Don't worry about people stealing your ideas. If your ideas are any good, you'll have to ram them down people's throats.

    -Howard Aiken
Re: Passing Any Range of Values to a Sub
by kabel (Chaplain) on Jul 02, 2003 at 05:19 UTC
    to live TMTOWTDT out, here are some other ways;
    one cannot recommend the first one, but the second is a clean way:

    1. a nice eval-trick, i think abigail-II used it in a sig
    eval join ('*', @list);
    2. something more functional?
    have a look at Language::Functional. the relevant bits are the functions foldl/foldr
    $x = foldl { shift() + shift() } 0, [1..6]; # 21
    this is the POD example.

    HTH a bit at least :-)
      print eval join (shift @ARGV eq 'add' ? '+' : '*', @ARGV);

      Fun, but maybe a bit unclear ...
Re: Passing Any Range of Values to a Sub
by David Caughell (Monk) on Jul 02, 2003 at 05:43 UTC

    Hi there.

    I'm also a student learning the language.

    Personally, I think it's best to always use:

    my $size = scalar(@testarr);

    rather than:

    my $size = @testarr;

    Because it's easier to understand and by naming something you exert a little more control over it. It also helps stress the difference between scalars and arrays, which is helpful for me as a student.

    I'd be interested in knowing what the more established users of the language think about using scalar() rather than using only the array.

    Any comments?

    David Caughell.
    Barrie, Ontario, Canada.

      If it's clearer for you and the people you work with, go ahead and use it. Maintainability and clear communication trump just about any other concern.

      I personally don't care for it because I'm used to context. It's effectively a no-op, and, if I don't have to worry about the maintainability burden to someone who doesn't have a firm grip on context (and I usually don't), I prefer to leave out unnecessary code.

      You'll probably find yourself using it less and less often as you continue learning.

        That makes sense, thanks a lot!

        I'd prefer to send a message like this privately, but I don't think I can do that. If there's a way of sending PMs here, someone please send me one and let me know!! :)

        Update: to send someone a private message, just type /msg username message in the chatter box and hit "talk".

        --Dave