Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

Array Processing

by Rajeshk (Scribe)
on Oct 08, 2005 at 07:53 UTC ( [id://498382]=perlquestion: print w/replies, xml ) Need Help??

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

Dear monks,

I want to remove the current processing element from the array

i used the following script. but i could not get correct solution.

Could you plz give me the solution and what's wrong in my script.

Thanks,
Rajesh.K


The script is:


local $, = "\n"; local $\ = "\n"; my @a = (1..5); foreach (@a){ $a = shift @a; print "Deleted value: $a"; } print "\nDeleted Array Content: ", @a;



After running this, the output is

Deleted value: 1 Deleted value: 2 Deleted value: 3 Deleted Array Content: 4 5

20051008 Janitored by Corion: Added code tags

Replies are listed 'Best First'.
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.


    Dave

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.

    Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
    How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart
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 = ( );
      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.

      blokhead

        Or use the comma operator: while (@a and my $a = shift @a, 1) { ... }

        (Updated code is in bold)


        Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
        How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart

      As mentioned by others, that will fail if the array may contain false or undefined values. You can do something similar by keeping the entire assignment in list context and making sure to assign a list:

      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.

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
Re: Array Processing
by guha (Priest) on Oct 08, 2005 at 15:44 UTC

    I would look at the problem from another side ;-)

    #!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
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.

      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."


      Dave

Re: Array Processing
by EvanCarroll (Chaplain) on Oct 08, 2005 at 08:40 UTC
    A better question is why are you trying to destroy your array. Why not modify the values in place, rather than shift them off; or, leave them alone and set @arr = (); or such. Shifting is a processes that while fast isn't needed here. And, any for loop will either require you increment a counting variable (fast), or copy the array (slow). A while loop would work but would still be bad practice because you would have to completly destroy the array thus making it evaluate to 0 in scalar context, or exit the loop (last;) from within the body of the loop.

    Please submit the rest of your code, I can almost assure you this is bad practice. Allow us to criticize!


    Evan Carroll
    www.EvanCarroll.com
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;
      Same Effect:
      perl -le'@_=1..5;undef @_;print scalar@_;' 0 perl -le'@_=1..5;@_=();print scalar@_;' 0


      Evan Carroll
      www.EvanCarroll.com
Re: Array Processing
by EvanCarroll (Chaplain) on Oct 08, 2005 at 08:11 UTC
    Your trying to modify the same array your looping over and perl doesn't like that.
    Try this instead, change:
    foreach (@a){
    To
    foreach ( @{[@a]} ) {


    Evan Carroll
    www.EvanCarroll.com

      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 ;)


      Dave

        Why do you mention the rest of his life here? Is it related to the question or the answer? Being rude is much worse than anything.

      A reply falls below the community's threshold of quality. You may see it by logging in.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others admiring the Monastery: (6)
As of 2024-03-28 13:45 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found