Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

Learning Perl - Question About Using a For Loop To Pull Data From an Array

by aUserName (Initiate)
on Apr 01, 2016 at 16:48 UTC ( [id://1159311]=perlquestion: print w/replies, xml ) Need Help??

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

At the end of this post is a simple script I wrote which was from one of the exercises in the learning perl book.

Basically there is an array with names in it, the user enters numbers, and once they hit crtl D (EoF) the script returns the corresponding value in the array.

I.e., if the array has fred betty barnet dino wilma pebbles bam-bam as values and the user enters 1 and 3, they should get fred and barnet back.

My code works, but I do not know why. In the foreach loop, I created $coutner (@userNumber) which means that if the user entered two numbers, $counter would = 2 and the loop would run twice.

The next line says to print $names $counter - 1

This works fine, the user is correctly given the name for the corresponding number they enter.

So if the user enters 1 and 3 they get fred and barnet back.

What is confusing me is why is $names $counter - 1 giving the correct entry. If the user enters 2 values, such as 1 and 3, $counter would = 2, not 1 or 3.

I verified this to be true by using print $counter and the end of the script during some of my tests.

Yet, when print $names $counter -1 is used and the user enters 1 and 3, fred and barnet are returned even though $counter = 2, not 1 or 3.

I would think that instead, since $counter = 2, the user would get fred and betty back, not fred and barnet.

Can anyone explain what I am missing?

#! perl ############ This script takes the number a user entered and returns t +he corrseponding name. ##### Create the list of names @names = qw / fred betty barnet dino wilma pebbles bam-bam /; ### # test to make sure @names is define correctly # # print @names; ### ###### Greet user and get the number from the user. print "\nWelcome to the name program.\nPlease enter a number or number +s and hit crtl + D when done: "; @userNumber = <STDIN>; ### test to dump array # # print @userNumber; ### ### test to see what $counter would be when used in foreach loop #$counter = @userNumber; #print $counter; ### ###### Return a name based on the number provided. foreach $counter (@userNumber) { print "\nthe name is: $names[$counter - 1] "; }
  • Comment on Learning Perl - Question About Using a For Loop To Pull Data From an Array
  • Download Code

Replies are listed 'Best First'.
Re: Learning Perl - Question About Using a For Loop To Pull Data From an Array
by Marshall (Canon) on Apr 01, 2016 at 17:24 UTC
    Well for one, this line $counter = @userNumber; causes the value of $counter to be set to the number of elements in @userNumber. This is the same as $counter = scalar @userNumber;

    The foreach loop loops over the contents of @userNumber. Change print $counter; to print "@userNumber\n"; to see the contents of @userNumber instead of the number of things in @userNumber.

    Update: looks like some issues here also: @userNumber = <STDIN>; You need a loop here to push the number entered by the user to @userNumber. CTL-D is an EOF. So something like this should work: while (<STDIN>){push @userNumber, $_);}. I didn't run your code, but I think the above will solve a couple of issues. another Update: I did run the code and well @userNumber = <STDIN>; does indeed work. It just looked weird to me as I never use CTL-D (or CTR-Z on Windows) to terminate user input. But a while loop is the right kind of loop because as you write more code, you will want to end the loop on a blank line or some other thing like zero and that condition will go in the while statement. Users don't do well with control characters.

      No, you don't need a loop to create the array. The OP indicated that the program worked, only that he did not understand why. The ctrl-D does indicate this should be run in a *nix environment, not a Windows one. I'm with you in that I would never write it this way.

      But God demonstrates His own love toward us, in that while we were yet sinners, Christ died for us. Romans 5:8 (NASB)

      Thank you for your reply. As you mentioned the script works fine. This is an example from a textbook which is why it may seem odd and not look like a real world script.

      I am not having any issues with the code, rather I am trying to understand how the for loop is using the $counter variable to pull out the actual number that user entered when it appears that $counter is being used as a scalar.

      For example if the user enters 1 and 3 they get fred and barnet back which are the first and third items in the array respectively.

      What is confusing me is why is $names $counter - 1 giving the correct values back. If the user enters 2 values, such as 1 and 3, $counter would = 2, not 1 or 3 since it is being treated as a scalar. Yet the script correctly returns the 1st and 3rd item in the array instead of the the 1st and 2nd item of the array.

      Thank you for your help.

        It seems that the name chosen, $counter, is confusing you.

        It is being used as an INDEX, rather than as a traditional COUNTER.

        The array is indexed from 0 to (n-1). (Updated to n-1)(Thanks, Corion)

        You need to subtract one from the value entered, in order to get the equivalent index for a zero-based array.

        Hopefully, that clarifies things.

        FYI, Fred's friend is named Barney, not barnet. and Barney's kid is Bamm-Bamm.

                This is not an optical illusion, it just looks like one.

        What this foreach $counter (@userNumber){} does is assign $counter to each value in the array @userNumber. It does this on a first in, first out basis. If the user entered say 1,3,2.. The first loop $counter = 1, then $counter =3, then on last loop, $counter = 2. Before print "\nthe name is: $names[$counter - 1], add a statement, print "counter = $counter\n";. And you can see what counter is. Use some non-sequential test cases.

        Part of the problem here is a rather poor choice of names for "counter". $name_number or something similar would have been better. There is no "count" function of $counter! It is being used as a 1 based index to the names array. Perl starts arrays at 0, not one, so that is what $counter-1 is all about. Hopefully that makes sense?

Re: Learning Perl - Question About Using a For Loop To Pull Data From an Array
by GotToBTru (Prior) on Apr 01, 2016 at 20:01 UTC

    Learning to use the Perl debugger will help you with understanding stuff like this. You can inspect the values of variables and see how instructions change them. In particular, you could put a watchpoint on $counter so you would see the values being used.

    I hate to see such sloppy programs being used as examples!

    But God demonstrates His own love toward us, in that while we were yet sinners, Christ died for us. Romans 5:8 (NASB)

      I have never used perl before and I am only on the third chapter of the book so I had not known about the watch function.

      Thank you for taking the time to provide a link to the documentation for me.

        This book is highly, very highly suspect if it presents code like this as examples!

        Print is your main friend. I very seldom use the debugger, even when developing C code. The debugger is yet another thing to learn at this early stage. I recommend getting good at writing print statements. Look at the difference say between: print "@names\n"; and print @names;.

        As another tip for you, add the statement use warnings as the second line in the code. Run the code using some bogus input number like 99, both with and without this statement. In one case, you just get a blank output and in the other you get a run time warning that something went wrong and a clue to what line did it!

        I don't want to overwhelm you with advice, please keep experimenting! You are going GREAT so far.

Re: Learning Perl - Question About Using a For Loop To Pull Data From an Array
by tangent (Parson) on Apr 01, 2016 at 22:06 UTC
    If the user enters 2 values, such as 1 and 3, $counter would = 2, not 1 or 3 since it is being treated as a scalar. Yet the script correctly returns the 1st and 3rd item in the array instead of the the 1st and 2nd item of the array.
    When $counter is used to access elements in the @names array it is not a static value, it takes on the value of each element in @userNumber in turn. Maybe this helps:
    my @names = qw / fred betty barnet dino wilma pebbles bam-bam /; my $total = scalar @names; print "number of elements in array: $total\n"; my $first = 0; print "index number of first element: $first\n"; my $last = $#names; print "index number of last element: $last\n"; for my $index ( $first .. $last ) { print "index $index: $names[ $index ]\n"; } my @userNumber = qw / 1 3 /; my $counter = @userNumber; print "counter is: $counter\n"; print "Start loop...\n"; foreach $counter ( @userNumber ) { print "counter is: $counter\n"; my $index = $counter - 1; print "\t$counter - 1 = $index: $names[ $index ]\n"; }
    OUTPUT:
    number of elements in array: 7 index number of first element: 0 index number of last element: 6 index 0: fred index 1: betty index 2: barnet index 3: dino index 4: wilma index 5: pebbles index 6: bam-bam counter is: 2 Start loop... counter is: 1 1 - 1 = 0: fred counter is: 3 3 - 1 = 2: barnet
Re: Learning Perl - Question About Using a For Loop To Pull Data From an Array
by haukex (Archbishop) on Apr 02, 2016 at 09:17 UTC

    Hi aUserName,

    In addition to what the others have said, I'd strongly recommend you add use strict; in addition to use warnings; at the top of your code. strict enforces a style of coding that is less prone to errors, which can be very helpful when you're learning. It's also a good habit to learn early, in fact, recent versions of Perl turn it on by default when you say use v5.12; (or higher) at the top of your code. warnings will give you warnings that are almost always helpful in tracking down (potential) bugs.

    When you first turn on strict, you'll need to change your script so that your variables are declared with my. The page Use strict and warnings explains this.

    Another very helpful thing for learning Perl is putting use diagnostics; at the top of your script. diagnostics will expand any error messages you get into longer explanations. I'd also recommend you look at the Basic debugging checklist.

    One more thing about your code: When you write @userNumber = <STDIN>;, then each element of @userNumber is a string that still contains the newline "\n" after the number (from pressing Return after each input). Your code works because Perl is silently ignoring that newline when you write $counter-1 and convert the string to a number, but it may cause problems when you do other things with the user input - try changing the print in the loop to print "the name $counter is: $names[$counter-1]\n"; and you'll see the newline as part of the output! That's why there's the helpful function chomp which removes the newlines for you. Either write chomp(@userNumber);, or you can combine the reading with the chomping into the perlish idiom chomp(my @userNumber = <STDIN>);.

    Hope this helps,
    -- Hauke D

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others having an uproarious good time at the Monastery: (4)
As of 2024-04-26 00:21 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found