Re: Array Processing
by davido (Cardinal) on Oct 08, 2005 at 08:11 UTC
|
Change your loop as follows, and the script will work properly:
local $" = "\n";
my @array = ( 1 .. 5 );
while( @array ) {
my $item = shift @array;
print "Removed value $item from the front of \@array\n";
}
print "Deleted array content:\n@a\n";
foreach iterates over the array's elements, but you keep changing them, and that has the potential of driving you mad. while() continues looping as long as its conditional is true. @a evaluated in scalar context returns the number of elements remaining in the array. This is a much better test, because as soon as there are zero elements remaining, the while() loop's conditional becomes false, and you stop looping.
| [reply] [Watch: Dir/Any] [d/l] [select] |
Re: Array Processing
by japhy (Canon) on Oct 08, 2005 at 15:32 UTC
|
This leads to a clever golf trick, by the way. To get only the upper half of an array, use shift@a for@a, and to get the bottom half, use pop@a for@a.
| [reply] [Watch: Dir/Any] [d/l] [select] |
Re: Array Processing
by liverpole (Monsignor) on Oct 08, 2005 at 13:36 UTC
|
To elaborate further on what davido and grandfather have said -- you can make it nicely terse using the syntax "while (my $a = shift @a)", as in:
#!/usr/bin/perl -w
# Strict
use strict;
use warnings;
# Main program
my @a = (1..5);
while (my $a = shift @a) {
print "Deleted value: $a\n";
}
Now that you are checking for the changing condition (the individually shifted-off element), you will get what you expect. If you're not going to use any of the values from the array, then as EvanCarroll pointed out, the easier/quicker way to delete all of @a would be:
@a = ( );
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
while (my $a = shift @a) {
This won't work if the array contains anything that is false in boolean context, like 0, '', or undef. You really need to explicitly check the array's length somehow.
If you're really intent on squeezing the assignment into the while loop, try:
while ( (my $a, @a) = @a ) {
This works because when evaluated in scalar context, the list assignment ( (list) = something ) returns the number of items in the right-hand side list. This little obscurity of syntax is probably one of the toughest to come to grips with, but it can be quite elegant at times.
In this case, I wouldn't actually use it since it reassigns most of @a to itself every time and is very inefficient in that respect.
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
|
|
|
|
|
|
while( my ( $a ) = @a ? shift @a : () ) {
# ...
}
Note the parens around $a: they make all the difference. They put the my (and thus the entire expression) in list context, and so the boolean value depends on the number of elements assigned. The ternary operator contortion on the right side ensures that when @a is empty, an empty list is assigned, so the loop aborts correctly.
Try putting empty strings, zeros or undefs in the array: you’ll find it will always do exactly what it’s supposed to.
However, elegant is not what it is. I’d much rather use while( @a ) and do a separate shift – far fewer subtleties to contend with.
Makeshifts last the longest. | [reply] [Watch: Dir/Any] [d/l] |
Re: Array Processing
by bradcathey (Prior) on Oct 08, 2005 at 14:26 UTC
|
Just this week I ran into a situation where I needed to be more systematic in element removals. Reading the array section in Q&A proved to be enlightening. Note the use of splice here.
Quoting from the Q&A
my $index = 0;
while ($index <= $#items ) {
my $value = $items[$index];
print "testing $value\n";
if ( $value == 1 or $value == 3 ) {
print "removed value $value\n";
splice @items, $index, 1;
} else {
$index++;
}
}
I tried using delete (which I thought was just for hashes) but got zero results, though the Perl ref says it's one of the methods. But maybe I'm not quite getting it.
—Brad "The important work of moving the world forward does not wait to be done by perfect men." George Eliot
| [reply] [Watch: Dir/Any] [d/l] [select] |
Re: Array Processing
by guha (Priest) on Oct 08, 2005 at 15:44 UTC
|
#!perl
use strict;
use warnings;
local $, = "\n";
local $\ = "\n";
my @a = (1..5);
foreach (reverse @a){
$a = pop @a;
print "Deleted value: $a";
}
print "\nDeleted Array Content: ", @a;
__DATA__
Deleted value: 5
Deleted value: 4
Deleted value: 3
Deleted value: 2
Deleted value: 1
Deleted Array Content:
Working backwards and pop-ing, I don't change index of the remaining items in the array.
The deletion could even be conditional using splice as in:
#!perl
use strict;
use warnings;
local $, = "\n";
local $\ = "\n";
my @a = (0..5);
foreach (reverse @a){
next unless $_ % 2;
splice(@a, $_, 1);
print "Deleted value: $_";
}
print "\nDeleted Array Content: ", @a;
__DATA__
Deleted value: 5
Deleted value: 3
Deleted value: 1
Deleted Array Content:
0
2
4
| [reply] [Watch: Dir/Any] [d/l] [select] |
Re: Array Processing
by GrandFather (Saint) on Oct 08, 2005 at 08:12 UTC
|
Change the foreach to a while. You're not using the itteration value anyway.
I don't understand why, but the problem is to do with the way the @a is evaluated on each pass through the loop. The behaviour may be version dependent even.
Perl is Huffman encoded by design.
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
This doesn't exactly answer why, but the behavior is documented in perlsyn:
If any part of LIST is an array, foreach will get very confused if you add or remove elements within the loop body, for example with splice. So don't do that.
As for "why", I think that's best answered with a question: "What do you think should happen if you remove elements as you iterate over them?" Since that question has no good answer, the answer to 'why' is "Because there's no good single answer to the question of what should happen."
| [reply] [Watch: Dir/Any] |
Re: Array Processing
by EvanCarroll (Chaplain) on Oct 08, 2005 at 08:40 UTC
|
| [reply] [Watch: Dir/Any] |
Re: Array Processing
by ioannis (Abbot) on Oct 09, 2005 at 03:49 UTC
|
There is little need to use for, while, or
recursion to delete all elements of @a. We could instead
use the undef operator:
my @a= (1..5) ;
undef @a;
And since the range operator is evaluated in array context, we could also drop the parens:
my @a = 1..5;
undef @a;
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
perl -le'@_=1..5;undef @_;print scalar@_;'
0
perl -le'@_=1..5;@_=();print scalar@_;'
0
Evan Carroll www.EvanCarroll.com
| [reply] [Watch: Dir/Any] [d/l] |
Re: Array Processing
by EvanCarroll (Chaplain) on Oct 08, 2005 at 08:11 UTC
|
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
Only if you've been forced to only use one type of loop the rest of your life (borrowing a concept from a recent poll). Your solution works by making a duplicate copy of the array. It results in the original array not being modified, so if the intent is to one by one remove all elements of @a, the intent will never be satisfied. Instead, one by one, all elements of a copy of @a will be removed, and @a remains unchanged.
Fortunately we live in a world with alternatives to foreach ;)
| [reply] [Watch: Dir/Any] [d/l] |
|
| [reply] [Watch: Dir/Any] |
|
|
A reply falls below the community's threshold of quality. You may see it by logging in.
|