Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Can't grab $ARGV[n]

by OxYgEn (Acolyte)
on Mar 05, 2004 at 00:41 UTC ( #334082=perlquestion: print w/ replies, xml ) Need Help??
OxYgEn has asked for the wisdom of the Perl Monks concerning the following question:

I'm pretty new at perl. Today, I decided I would challenge myself and make a script bigger than just 4 or 5 lines. I came up with the idea to make a plane geometry calculator, in which the args will indicate which figure and operations to do as well as any prerequisite numbers to do them.

Readmore tags don't seem to be working, so I'm going to post the readmores inside these little dash things. Treat it like a readmore.

-----------------------------IN READMORE--------------------------------------------------
In the script, the possible figures are tri, cir, sqr, and rec. The possible operations are area, peri(perimeter), or hypo(hypotenuse). Based on the combination of these two args, the script will assign a value to $reqargs, which indicates the number of required arguments following the operation. And now, based on the combination of the three pieces of data you have so far, it generates a number in the scalar $refsum. The script then has an enourmous array of if/elsif/else cases built around the possible amount for $refsum, and based on this scalar it will determine the operation, execute it, and store the results in $answer, which is printed at the end. And here's readmore inside a readmore about how refsum is determined.
-----------------------------IN READMORE--------------------------------------------------
REFSUM is built off place values. It is first declared as zero before anything is done to it. Then, if you specified a triangle, circle, square, or rectangle, it will add 0.1, 0.2, 0.3, or 0.4 to $refsum, respectively. The same goes for all other input. I think that's a pretty good explanation...

-----------------------------OUT READMORE-------------------------------------------------
-----------------------------OUT READMORE-------------------------------------------------

Everything in the script seems to be running fine, except for the fact that it always tells me I haven't specified any arguments. I will put my code below. If you don't want to read the code you might be able to find the problem in the readmores; I'm pretty sure it has something to do with the way i use $ARGV.

#!usr/bin/perl -w use warnings; print "Welcome to Will's Plane Geometry Calculator.\nThe first argumen +t should be tri, cir, rec, or sqr.\nThe second argument should be area, peri, or hypo.\nIf you give hypo as the second argumen +t, the next two arguments should be the known sides of the traingle.\nthe peri arg will also find circumference if cir is giv +en for the figure. Specify the diameter to find this.\nPi is approximated at 3.141592654(simply change my pi if a diff +erent approximation is needed)\nTo find area of a circle specify the radius.\nI think you can figure out the rest.\n", " +-" x76, "\n"; my $pi=3.141592654; if (defined $ARGV[0] && defined $ARGV[1] && defined $ARGV[2] && define +d $ARGV[3]) { #MasterIf; keep all commands inside #################################### #Define our variables according to arguments #First off is ARG 0 (figure) if ($ARGV[0]=="tri") { my $figure="triangle"; } elsif ($ARGV[0]=="cir") { my $figure="circle"; } elsif ($ARGV[0]=="rec") { my $figure="rectangle"; } elsif ($ARGV[0]=="sqr") { my $figure="square"; } # #Next is ARG 1 (operation) # if ($ARGV[1]=="area") { my $op="area"; if ($figure=="rectangle") { my $reqargs=2; } elsif ($figure=="square") { my $reqargs=1;} elsif ($figure=="triangle") { my $reqargs=2; } elsif ($figure=="circle") { my $reqargs=1; } } elsif ($ARGV[1]=="peri") { my $op="perimeter"; if ($figure=="rectangle") { my $reqargs=2; } elsif ($figure=="square") { my $reqargs=1; } elsif ($figure=="triangle") { my $reqargs=3; } elsif ($figure=="circle") { my $reqargs=1; } } elsif ($ARGV[1]=="hypo") { my $op="hypotenuse"; my $reqargs=2; if ($figure!="triangle") { die "Hypotenuse calculations only work +with argument tri.\n"; } } # #Define REFSUM for easier case management later #The following cases are to convert defined scalars into numbers. B +ased on the number, the script will decide what to do. # my $refsum=0; if ($figure=="triangle") { $refsum=$refsum+0.1 } elsif ($figure=="circle") { $refsum=$refsum+0.2 } elsif ($figure=="rectangle") { $refsum=$refsum+0.4 } elsif ($figure=="square") { $refsum=$refsum+0.3 } if ($op=="area") { $refsum=$refsum+1 } elsif ($op=="peri") { $refsum=$refsum+2 } elsif ($op=="hypo") { $refsum=$refsum+3 } if ($reqargs==1) { $refsum=$refsum+10 } elsif ($reqargs==2) { $refsum=$refsum+20 } elsif ($reqargs==3) { $refsum=$refsum+30 } # #Next is ARG 2 (first num) #This is where it starts to get complicated # print "\n INFO PRINTOUT FROM SCALARS: \n FIGURE: $figure \n OPERATIO +N: $op \n REQUIRED ARGS: $reqargs\n\n"; if ($refsum<10) { die "[err] REFSUM is less than 10; invalid number."; } if ($refsum==21.1) { if (defined $ARGV[4]) { my $base=$ARGV[3]; my $height=$ARGV[4]; my $answer=$base*$height; } else { die "Did not specify height of triangle\n"; } } elsif ($refsum==32.1) { if (defined $ARGV[5] && defined $ARGV[4]) { my $sidesone=$ARGV[3]; my $sidestwo=$ARGV[4]; my $sidesthree=$ARGV[5]; my $answer=$sidesone+$sidestwo+$sidesthree; } else { die "Did not specify all three sides\n"; } } elsif ($refsum==23.1) { if (defined $ARGV[4]) { my $firstrefraw=$ARGV[3]; my $firstrefuse=$firstrefraw*$firstrefraw; my $secondrefraw=$ARGV[4]; my $secondrefuse=$secondrefraw*$secondrefraw; my $answer=$firstrefuse+$secondrefuse; } else { die "Did not specify both sides\n"; } } elsif ($refsum==12.2) { my $diameter=$ARGV[3]; my $answer=$diameter*$pi; } elsif ($refsum==11.2) { my $radraw=$ARGV[3]; my $raduse=$radraw*$radraw; my $answer=$raduse*$pi; } elsif ($refsum==13.2) { print "[err] a 3 in the ones place value is reserved for triangles +.\n"; die "[err] invalid value for REFSUM ( $refsum )\n"; } elsif ($refsum==11.3) { my $asidesa=$ARGV[3]; my $answer=$asidesa*$asidesa; } elsif ($refsum==12.3) { my $asidesp=$ARGV[3]; my $answer=$asidesp*4; } elsif ($refsum==13.3) { print "[err] a 3 in the ones place value is reserved for triangles +.\n"; die "[err] invalid value for REFSUM ( $refsum )\n"; } elsif ($refsum==21.4) { if (defined $ARGV[4]) { my $length=$ARGV[3]; my $width=$ARGV[4]; my $answer=$length*$width; } else { die "Length and Width of rectangle not specified.\n"; } elsif ($refsum==22.4) { if (defined $ARGV[4]) { my $lengthraw=$ARGV[3]; my $lengthuse=$lengthraw*2; my $widthraw=$ARGV[4]; my $widthuse=$widthraw*2; my $answer=$lengthuse*$widthuse; } else { die "Length and Width of rectangle not specified.\n"; } elsif ($refsum==23.4) { die "Invalid number for REFSUM; reserved for triangles.\n"; } # #Calculations part is done, this part is just telling you the answer # if (defined $answer) { print "\n\n","-" x76,"\n","Calculations Completed Successfully!\n" +; print "Your answer is: $answer"; } else { print "\n\n","-" x76,"\n","Calculations Completed.\n Operation Uns +uccessful.\n Cause Unkown.\n"; } print "Due to stupidity of PERL: pi is $pi"; } else { die "Please define at least 4 arguments\n"; } + #Die if less than 4 args given
Thanks, ~OxYgEn

Comment on Can't grab $ARGV[n]
Download Code
Re: Can't grab $ARGV[n]
by runrig (Abbot) on Mar 05, 2004 at 00:48 UTC
    read perldoc perlop or some other documentation. String equality operator is 'eq', numerical equality is '=='. That's as far as I've examined your code, but that'll get you started...

    Update: another tip...use strict (use Super Search to search for "strict" in the subject of posts in Tutorials here also). Some of your variables are being set in one scope and used in another...and so have no value (my is a lexical scoping function, e.g. you have 4 different my $figure = ... lines, they each refer to different $figure variables which do not exist outside of the block they are declared in).

      If there were an error in scalar references, wouldn't the command window return some errors?

      And about using strict, I've tried it in a handful of other scripts and when I run them I always get these Global symbol $whatever requires explicit package name errors when I use it. Two thirds of the time, when I take the use strict; line out, it works. This is why I never use strict, though I'm sure it would help me if i better understood it.

        In when you are using strict you have to declare your varibles with the my key word. ie my $foo = "a bar"; then when you use it later on you can just say $foo = "two bars";.
        The warning you are getting appears when the variables have not been declared in this way. This is helpful beacuse if you mis-spell a variable then you will be warned of the fact. This has saved me about 250,000 times from stupid typos and the like. There are other reasons too I am sure, but this is why I use it.

        Hope this helps.
        If there were an error in scalar references, wouldn't the command window return some errors?
        No, perl let's you use strings as numbers. If it can't figure out a numerical context for your variable, then the value assumed is zero.
        This is why I never use strict, though I'm sure it would help me if i better understood it.
        By all means, take the time to understand it. Start with what is further down in this thread, the Super Search I suggested earlier, and the documentation.

        Update: Another helpful link

Re: Can't grab $ARGV[n]
by jarich (Curate) on Mar 05, 2004 at 04:29 UTC
    if (defined $ARGV[0] && defined $ARGV[1] && defined $ARGV[2] && define +d $ARGV[3]) { ... } else { #Die if less than 4 args given die "Please define at least 4 arguments\n"; }
    I presume from what you said above that you're always hitting the else from this code. Without knowing how you're calling your script I can't guess why this is going wrong. However I'll offer you some ideas on how you can generally improve your code.

    1.
    In the if statement I quoted above, all you're trying to see is whether there are 4 arguments. This can be done in a much neater way:

    if(@ARGV < 4) { die "Please define at least 4 arguments\n"; } # I must have 4 arguments at this point.
    Note that I've flipped your if/else statement here too. This helps you reduce the number of levels of indentation and lets you handle the error case immediately rather than in a completely unrelated piece of code.

    Of course what you really want is to check that @ARGV is less than 3 because in the case of a square you only ever ask for one more value.

    2.
    Use hashes! If you ever see yourself writing code like this:

    if ($ARGV[0]=="tri") { my $figure="triangle"; } elsif ($ARGV[0]=="cir") { my $figure="circle"; } ... if ($ARGV[1]=="area") { my $op="area"; if ($figure=="rectangle") { my $reqargs=2; } elsif ($figure=="square") { my $reqargs=1;} elsif ($figure=="triangle") { my $reqargs=2; } elsif ($figure=="circle") { my $reqargs=1; } }
    you should probably think: "hmm, this can be represented in a hash." For example:
    my $shape = shift @ARGV; # take first argument off ARGV my %shapes = ( "tri" => "triangle", "cir" => "circle", "req" => "rectangle", "sqr" => "square" ); my $figure = $shapes{$shape} or die "Unknown shape $shape\n";
    The advantage of this is that you can store other, more interesting information in the same hash which will save you time later.
    my $shape = shift @ARGV; # take first argument off ARGV my $op = shift @ARGV; # take second argument off ARGV my %shapes = ( "tri" => { name => "triangle", area => 2, # required number of args peri => 3, hypo => 2, }, "sqr" => { name => "square", area => 1, peri => 1, }, "rec" => { name => "rectangle", area => 2, peri => 2, }, "cir" => { name => "circle", area => 1, peri => 1, }, ); # Get full name of figure. my $figure = $shapes{$shape}{name} or die "Unknown shape: $shape\n"; # Determine how many arguments we require. my $reqargs = $shapes{$shape}{$op} or die "Unknown operator $op". " or $op does not work with shape $figure";

    3. Rethink your need for $refsum. This is one point where if/elsif/else would make your code a whole bunch more readable. And yes, you could even move the code for the if/elsif/else into your hash if you wanted to, but I thought it would be better to start out easy.

    4. As others have said, use strict. If you were using strict you would have been told that $answer was unknown where you check whether it's undefined. As others have said this is because you're doing this:

    elsif ($refsum==12.2) { my $diameter=$ARGV[3]; my $answer=$diameter*$pi; } # $answer stops existing HERE
    You need to declare $answer above that big mess and then do:
    elsif ($refsum==12.2) { my $diameter=$ARGV[3]; $answer=$diameter*$pi; }

    5. Double check your formulas. Since you're using $refsum it's hard to see what you're actually calculating but some of them are wrong. The area of a triangle is half what your code will report, for example.


    Anyway, I've put all of these ideas into practice and this is the script I get. Compare it closely to what you've done and feel free to ask about any changes.

    I hope this helps.

    jarich

      Taking that hash idea a bit further, you could end up at a dispatch table, something like the following:

      #!usr/bin/perl use strict; use warnings; sub req_args { my $nArgs = @ARGV; die "$_[0] argument(s) required but $nArgs given!\n" if $_[0] != $nArgs; } my $pi=3.141592654; my %dispatch = ( tri => { area => sub { req_args(2); return 0.5*$ARGV[0]*$ARGV[1]; + }, peri => sub { req_args(3); return $ARGV[0]+$ARGV[1]+$ARGV +[2]; }, hypo => sub { req_args(2); return sqrt($ARGV[0]*$ARGV[0]+ +$ARGV[1]*$ARGV[1]); }, }, cir => { area => sub { req_args(1); return $ARGV[0]*$ARGV[0]*$pi; + }, #radius peri => sub { req_args(1); return $pi*$ARGV[0]; + }, #diameter hypo => sub { die "Option 'hypo' only allowed for triangl +e." }, }, rec => { area => sub { req_args(2); return $ARGV[0]*$ARGV[1]; + }, peri => sub { req_args(2); return 2*($ARGV[0]+$ARGV[1]); + }, hypo => sub { die "Option 'hypo' only allowed for triangl +e." }, }, sqr => { area => sub { req_args(1); return $ARGV[0]*$ARGV[0]; + }, peri => sub { req_args(1); return 4*$ARGV[0]; + }, hypo => sub { die "Option 'hypo' only allowed for triangl +e." }, }, ); die "You need to specify at least 3 args." if @ARGV < 3; # $what to calculate on $which type of figure my ($which, $what) = (shift, shift); die "Unknown type '$which' of figure." unless exists $dispatch{$wh +ich}; die "Unknown type '$what' of calculation." unless exists $dispatch{$wh +ich}{$what}; print "The answer is ", $dispatch{$which}{$what}(), ".\n";

      -- Hofmator

Re: Can't grab $ARGV[n]
by Crian (Chaplain) on Mar 05, 2004 at 13:13 UTC
    There are so much good points shown by other monks, but I have an other tip for you:

    instead of

    my $pi=3.141592654;

    you could use

    use constant PI => 3.141592654;

    (in case you don't want to change PI in your program ;) )

    Update: And you could use
    print << 'END', "-" x76, "\n"; Welcome to Will's Plane Geometry Calculator. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The first argument should be tri, cir, rec, or sqr. The second argument should be area, peri, or hypo. If you give hypo as the second argument, the next two arguments should be the known sides of the traingle. The peri arg will also find circumference if cir is given for the figure. Specify the diameter to find this. Pi is approximated at 3.141592654 (simply change my pi if a different approximation is needed). To find area of a circle specify the radius. I think you can figure out the rest. END


    That is much clearer to read for me.

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (11)
As of 2014-07-25 17:37 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite superfluous repetitious redundant duplicative phrase is:









    Results (174 votes), past polls