negzero7 has asked for the wisdom of the Perl Monks concerning the following question:
Hi, I wrote up a simple add() and multiply() subroutine script and wanted to have it looked over. I would like it to be as efficient and correct as possible.
As it is now, it works correctly with no errors, just wanted to make sure it is proper form. Thanks for any advice.
use warnings; use strict; my $i = $#ARGV; if ($ARGV[0] eq "add") { my $rtn = add($ARGV[1]..$ARGV[$i]); print "The sum is: $rtn"; } elsif ($ARGV[0] eq "multiply"){ my $rtn = multiply($ARGV[1]..$ARGV[$i]); print "Their product is: $rtn"; } sub add { my ($sum); $sum=0; foreach $_(@_) { $sum +=$_; } return $sum; } sub multiply { my $product=1; foreach $_(@_) { $product*=$_; } return $product; }
|
|---|
| Replies are listed 'Best First'. | |
|---|---|
|
Re: Simple add and multiply subroutines
by jwkrahn (Abbot) on Apr 08, 2008 at 02:13 UTC | |
You have a major error in that you are using the range operator which will not work correctly, for example:
You should probably shift the first argument off @ARGV, something like this:
| [reply] [d/l] [select] |
by negzero7 (Sexton) on Apr 08, 2008 at 02:31 UTC | |
I think I see what you mean, but why would I put $i in the if conditional? Like you have:
I don't understand why it goes there. | [reply] [d/l] |
by Anonymous Monk on Nov 16, 2009 at 01:19 UTC | |
| [reply] |
|
Re: Simple add and multiply subroutines
by mr_mischief (Monsignor) on Apr 08, 2008 at 03:19 UTC | |
You can declare and initialize $sum in one line. my $sum = 0; If you shift the operation off of @ARGV, you can then use the more natural notation for calling the subs: Read more... (449 Bytes)
Some people place the else or elsif on the same line as the right curly bracket, and some place it on the next. I'm not sure you'll ever see someone skip a line between an if and an else/elsif entirely, and that's probably a good thing. Although Perl is only in rare cases sensitive to the presence or absence of white space, it helps readability of your code if you use a space between disparate tokens. Read more... (826 Bytes)
A for loop and a foreach loop are the same thing. There was a widespread habit once upon a time of using "for" only with a C-style set, check, update style loop and using "foreach" for iterating over a list. Many people just use "for" in both cases, but if you're working under a professor or with a group of other programmers who tend to use "foreach", then it's probably a good idea to follow your local convention. Another solution to your problem, in which the main program is very short, is this: Read more... (1122 Bytes)
Other options of course exist. One that you'd likely catch all kinds of crap from a professor (or anyone interested in maintainability, likely) is this: Read more... (920 Bytes)
| [reply] [d/l] [select] |
|
Re: Simple add and multiply subroutines
by ysth (Canon) on Apr 08, 2008 at 02:21 UTC | |
But it would be nicer to separate your arguments up front:
| [reply] [d/l] [select] |
by negzero7 (Sexton) on Apr 08, 2008 at 02:41 UTC | |
Woot I think I got it. Check it out (I also added comments)
How does that look? | [reply] [d/l] |
|
Re: Simple add and multiply subroutines
by aufflick (Deacon) on Apr 08, 2008 at 05:39 UTC | |
So the range operator lets you say: 1..5 which is equivalent to the list: 1, 2, 3, 4, 5 So if, say, your ARGV array in the original example was ('add', 1, 7, 22, 5) then $ARGV[1]..$ARGV[$i] would evaluate to 1..5, ie: (1, 2, 3, 4, 5). Echoing oko1's comment about a dispatch table being the way to go, here is how I would write your script:
| [reply] [d/l] [select] |
|
Re: Simple add and multiply subroutines
by oko1 (Deacon) on Apr 08, 2008 at 04:03 UTC | |
Not that I'm disagreeing with all the errors/problems other people have pointed out, but this is really the canonical case for using a dispatch table.
-- Human history becomes more and more a race between education and catastrophe. -- HG Wells | [reply] [d/l] |
by jwkrahn (Abbot) on Apr 08, 2008 at 06:35 UTC | |
| [reply] | |
by oko1 (Deacon) on Apr 08, 2008 at 20:05 UTC | |
Nor engineering notation, nor imaginary numbers, nor Roman numerals either. It will, however, handle the integers as shown in the original request. :) String eval... OK, you're right. Easy enough to fix, and I see (behind the cut tags) that the poster just before me already did that. -- Human history becomes more and more a race between education and catastrophe. -- HG Wells | [reply] |
|
Re: Simple add and multiply subroutines
by NetWallah (Canon) on Apr 08, 2008 at 06:30 UTC | |
If I may add my $0.02, there is something to be said for minimising repetition of similar looking code, particularly if it is likely that the number of repetitions may increase. With that in mind, may I offer: Now, you are on your way to implementing a reverse polish engine ... "As you get older three things happen. The first is your memory goes, and I can't remember the other two... " - Sir Norman Wisdom | [reply] [d/l] |
by dragonchild (Archbishop) on Apr 13, 2008 at 02:02 UTC | |
Except, even that's too much repetition. Learn CPAN, young padawan. It is rare that someone hasn't already solved at least part of whatever it is you're looking at. Writing core Perl for more than 50% of a program is a sign of "I don't know CPAN," not brilliance. Update: Fixed typo s/op/ops/ inside eval. My criteria for good software:
| [reply] [d/l] [select] |
by NetWallah (Canon) on Apr 13, 2008 at 16:52 UTC | |
Education never stops. Update:Code Re-written incorporating dragonchild and mr_mischief's ideas: Note that the eval used is a string eval, using only program variables (no user-supplied data). This eval can be performed ahead of time, or JIT (as programmed). The eval is performed only once per function-name. Specifically, it would NOT be performed every time the user provides input (if the program had an input loop). Thanks to dragonchild, and mr_mischief for making this interesting/educational. "As you get older three things happen. The first is your memory goes, and I can't remember the other two... " - Sir Norman Wisdom | [reply] [d/l] |
by mr_mischief (Monsignor) on Apr 10, 2008 at 17:09 UTC | |
BTW, if you're really wanting to cut down on duplicated code and don't mind torturing the language a bit, you can do this:
I know, I know... It's ugly to do from 1 to 3 evals for each iteration and possibly another one or two at the end. The data structures could have clearer names. At least only the op you need gets its sub created. I did say the language would be tortured a bit, didn't I? It's kind of a fun little toy, though. | [reply] [d/l] [select] |
by oko1 (Deacon) on Apr 09, 2008 at 01:08 UTC | |
Might as well toss this in, then:
Despite jwkrahn's warning, I'd want to implement an "eval" wrapped around the operation, too - after carefully vetting the data. It's quite nice for those "Divide by zero" errors. -- Human history becomes more and more a race between education and catastrophe. -- HG Wells | [reply] [d/l] |