LinuxQuestions.org
Welcome to the most active Linux Forum on the web.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 08-12-2009, 11:25 AM   #1
indiancosmonaut
Member
 
Registered: Feb 2007
Posts: 65

Rep: Reputation: 15
PERL - example containing `shift`...a little issue


I'm new to Perl and I'm trying to read the array elements in a function.
When I use the below script, it's only displaying the first four.

Code:
use strict;
my $y = [100,200,300,400,500,600,700,800];

my $x = $y;

sub change_me {
	my $i = 0;
	my $k = 0;

	print "\nPrinting all the elements of the array : @_\n\n";


	foreach (@_) {
		print "Interation: $i\n";
		print "--------------\n";
		$i+=1;
		print "Now - @_\n";
		$k = shift(@_);
		print "Reading character: $k\n\n";
		$k = undef;
	}

}

# Calling the sub...
change_me(@{$x});
The output:

Code:
C:\Perl\bin>pr my_2

Printing all the elements of the array : 100 200 300 400 500 600 700 800

Interation: 0
--------------
Now - 100 200 300 400 500 600 700 800
Reading character: 100

Interation: 1
--------------
Now - 200 300 400 500 600 700 800
Reading character: 200

Interation: 2
--------------
Now - 300 400 500 600 700 800
Reading character: 300

Interation: 3
--------------
Now - 400 500 600 700 800
Reading character: 400
Don't understand why its not displaying all the elements...Please advice.
 
Old 08-12-2009, 12:20 PM   #2
Telemachos
Member
 
Registered: May 2007
Distribution: Debian
Posts: 754

Rep: Reputation: 60
It's not printing all the elements because you are tampering with the array while you are iterating over it.

The foreach keyword is a built-in iterator. It will automatically iterate over the array, assigning each item to $_ (unless you give another name for it to use, eg. foreach my $item (@array) will alias each item to $item).

While you are iterating over the array you do not want to shift items off it, since that changes the array in the course of the iteration.

So you have a choice: you can iterate using shift in a while loop or you can iterate using foreach. But you don't want to mix these two. Here's an example of the while loop:

Code:
sub iterate {
    my $copy = shift @_;
    print $copy;
	
    print "\nPrinting all the elements of @$copy\n";
	
    my $count = 1;
	
    while (my $item = shift @$copy) {
        print "Iteration: $count\n";
	print "-----------------\n";
	$count++;
	print "Now = @$copy\n";
	print "Reading character: $item\n\n";
    }
}
But the code you posted is odd in other ways. Why do you create an array reference, then copy it, then put it into a subroutine only to shift each item off and print it? What are you really trying to accomplish?
 
Old 08-13-2009, 04:45 AM   #3
indiancosmonaut
Member
 
Registered: Feb 2007
Posts: 65

Original Poster
Rep: Reputation: 15
Thanks you for looking into it Telemachos,

First of all, I understood the point that you made about foreach being an internal iterate...I read more on it. But what I don't understand is that the array is still showing 5 items left in the array so why doesn't foreach try to read them? (I'm may be asking the same question in a different way!) If you believe I shouldn't be wasting my time on this then please let me know because I don't want to beat around the bush for no good reason

Another interesting this is that it only prints n/2 (where n is even) and n/2+1 (where n is odd)!!

Secondly, the code looks odd because I'm just learning and trying to mix things in one code snippet. Digging the ground deep enough to build a decent house
 
Old 08-13-2009, 05:38 AM   #4
Telemachos
Member
 
Registered: May 2007
Distribution: Debian
Posts: 754

Rep: Reputation: 60
Quote:
Originally Posted by indiancosmonaut View Post
Thanks you for looking into it Telemachos,

First of all, I understood the point that you made about foreach being an internal iterate...I read more on it. But what I don't understand is that the array is still showing 5 items left in the array so why doesn't foreach try to read them? (I'm may be asking the same question in a different way!) If you believe I shouldn't be wasting my time on this then please let me know because I don't want to beat around the bush for no good reason

Another interesting this is that it only prints n/2 (where n is even) and n/2+1 (where n is odd)!!
Getting half-way is what you should expect since you are iterating over the array in double-time: once via the built-in foreach and once via shift. By the time you get half-way, you've run out of all your elements.

This should make clearer what is happening. Alter the foreach loop in your script so that it shows the state of the @_ array both before and after the shift, and so that it shows what's in the variable $k and the variable $_. The foreach mechanism is putting an item into $_ and you are putting a second item into $k. (Actually, the first time through the two are identical, but after that you've tampered with the array, so they change.)

Here's the changed code:

Code:
  foreach (@_) {
    print "Interation: $i\n";
    print "--------------\n";
    $i+=1;
    print "Before shift - @_\n";
    $k = shift(@_);
    print "After shift - @_\n";
    print "\$k is $k and \$_ is $_\n\n";
    $k = undef;
  }
And its output:

Code:
Printing all the elements of the array : 100 200 300 400 500 600 700 800

Interation: 0
--------------
Before shift - 100 200 300 400 500 600 700 800
After shift - 200 300 400 500 600 700 800
$k is 100 and $_ is 100

Interation: 1
--------------
Before shift - 200 300 400 500 600 700 800
After shift - 300 400 500 600 700 800
$k is 200 and $_ is 300

Interation: 2
--------------
Before shift - 300 400 500 600 700 800
After shift - 400 500 600 700 800
$k is 300 and $_ is 500

Interation: 3
--------------
Before shift - 400 500 600 700 800
After shift - 500 600 700 800
$k is 400 and $_ is 700
Look carefully at the output. The foreach loop iterates over an array by keeping track of its position in a 0-indexed array. You seem to think that the foreach loop should track the elements in the original array, but keeps track simply of where it is in sequence 0, 1, 2, 3, etc. Since you are shrinking the actual array as you go, you finish sooner than you expect. If you print out what's in $_, you can see where the foreach loop is during each iteration.
  1. In the first loop, $_ is whatever is at index 0 - namely 100. Then you remove an element.
  2. In the second loop, $_ is whatever is at index 1, but index 1 is now 300. It's crucial that you see how I know this. Look at the printout for that iteration before the shift. The items are (200, 300, 400, 500, 600, 700, 800). Count up, starting from 0, and '300' is at index 1. Do that for each iteration to see what the foreach loop is doing. The foreach loop skipped 200 entirely since it was moved to index position 0 and the loop has already done index 0. You also removed another element from the array.
  3. In the third loop, $_ is whatever is at index 2, but index 2 is now 500. The loop skipped 400. Again, you remove an element from the array. It's shrinking.
  4. In the fourth loop, $_ is whatever is at index 3, but index 3 is now 700. You remove another element from the array.
  5. The next loop through, $_ should be whatever is at index 4, but the array is (500, 600, 700, 800) so there is nothing at index 4 (the fifth element in a 0-indexed array), so we're done.

See also this post from another board for a variation on the same problem. (The OP there starts with a while loop not a foreach, but he also messes with the array while iterating over it.)

Last edited by Telemachos; 08-13-2009 at 05:47 AM.
 
Old 08-13-2009, 10:05 AM   #5
indiancosmonaut
Member
 
Registered: Feb 2007
Posts: 65

Original Poster
Rep: Reputation: 15
Much Appreciated Telemachos...with guys like you around I'll probably learn this language in half as much time!
 
Old 08-14-2009, 12:45 AM   #6
chrism01
LQ Guru
 
Registered: Aug 2004
Location: Sydney
Distribution: Rocky 9.2
Posts: 18,359

Rep: Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751Reputation: 2751
There's some good tutorials here: http://www.perlmonks.org/?node=Tutorials
Lang definition with examples/tutorials etc: http://perldoc.perl.org/
 
Old 08-14-2009, 04:40 AM   #7
indiancosmonaut
Member
 
Registered: Feb 2007
Posts: 65

Original Poster
Rep: Reputation: 15
Thanks! Chrism01
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Perl quoting issue resetreset Programming 8 01-10-2009 08:58 AM
Perl datescript Issue, missing perl module? stefaandk Programming 5 02-19-2006 10:55 PM
perl with BerkeleyDB issue? Xstack Programming 0 06-03-2005 07:09 PM
perl issue moonloader Slackware 2 03-27-2004 01:07 PM
Perl Script issue.... oicdn Linux - Software 2 03-24-2004 10:47 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 04:38 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration