Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine

process array with while loop

by reason2006 (Novice)
on Nov 09, 2006 at 17:06 UTC ( #583157=perlquestion: print w/replies, xml ) Need Help??

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

I've been using a while loop and the diamond operator to process arrays. I am in a perl training class this week and the teacher had never seen this done. I believe I read somewhere in a book or on the web on how to do it this way but cannot find it. Can someone clarify whether or not this is an accepted way to loop through an array? Thanks. Here's a code snippet:
@array = qw(blue, red, orange, brown); while (<@array>) { print $_; }

Replies are listed 'Best First'.
Re: process array with while loop
by grep (Monsignor) on Nov 09, 2006 at 17:16 UTC
    This is bad practice.If you read perlop: I/O Operators carefully you'll see < > are special for filehandles. You'll most likely run into weird problems if the list is empty.

    Also qw does not need (or want) commas. It should be more like:

    use strict; use warnings; my @array = qw( blue red orange brown ); foreach (@array) { print "$_\n"; }

    One dead unjugged rabbit fish later
      Thanks guys. I'll switch to using foreach.
        There is one time when you will want to use a while loop for an array (that I can think of) and that is if you are adding items to the array while iterating through it. for(each) will not reflect changes to the array once the loop has started. A good example of something like this is traversing a directory structure....
        #This code not tested my @dirs = '.'; while(my $dir = shift @dirs) { opendir(DIR,$dir); while(my $entry = readdir(DIR)) { if(-d $entry) { push @dirs, "$dir/$entry"; } else { #do something for files/links etc } } closedir(DIR); }
        Just thought I would point that out. for basically makes an in-memory copy of the list when it first starts.

                        - Ant
                        - Some of my best work - (1 2 3)

Re: process array with while loop
by ikegami (Patriarch) on Nov 09, 2006 at 17:09 UTC
    my @array = qw(blue red orange brown); # No commas in qw() foreach (@array) { print("$_\n"); }

    See perlsyn.

    <...> is used when reading from a file handle. It can also be used for globbing (as you are using it), but that's very fragile.

    Update: Just noticed the commas in qw(). Addressed this.

      To elaborate (because this foxed me, I thought <> must have different behaviour when passed an array), as far as I can tell, the reason this appears to work is a result of:
      1. glob("pattern") returns "pattern" if no files match the pattern (this includes patterns which no metacharacters, such as 'blue');
      2. In a scalar context <pattern> is magic - it keeps state and returns each successive value of glob("pattern"). For example, create files touch tt1 tt2 tt3 and then run:
        print scalar <tt?>, "\n" for 1..3; # prints tt1, tt2 and tt3
        Note that (like the flipflop operator) each seperate instance of <pattern> appears to keep its own internal state.
        print scalar <tt?>, "\n"; print scalar <tt?>, "\n"; print scalar <tt?>, "\n"; # prints tt1, tt1, tt1
        And to drive the point home, fun with closures:
        my $s = sub { return scalar <tt?>; }; print $s->(), "\n"; print $s->(), "\n"; print $s->(), "\n"; # prints tt1, tt2, tt3
      So...this will "work" provided none of the array elements is a string which both contains a glob metacharacter and matches a file as a glob. (If the array elt doesn't contain any such chars, it doesn't matter if it matches or not - you'll get back the pattern).
Re: process array with while loop
by ww (Archbishop) on Nov 09, 2006 at 17:14 UTC
    But, as written (eg, with commas), it works if it's not "THE (emphasis supplied, though absent from the original) accepted way" it might be less-preferred, but NOT UN-useable, so long as you know what you're you're doing (why and on what sort of data).


    updated with more qualifications after ikegami slapt me upside the head re the commas (D'oh on me) and
    <STRUCK> the boneheaded misstatement above after checking perlop which VERY SPECIFICALLY says,
    A common mistake is to try to separate the words with comma or to put comments into a multi-line qw-string. For this reason, the use warnings pragma and the -w switch (that is, the $^W variable) produces warnings if the STRING contains the "," or the "#" character.

    This ain't my day YAUpdate: ikegami points out that "Because of the spaces after the commas, the array does have multiple elements."

    and just for the record, reason2006 's clarification below is noted without further dumb-ness from /me

    The language in perlop also points out - or so it now seems to me - that OP's example is NOT "using a while loop and the diamond operator to process arrays" but is rather processing a one element array containing a list or string and relying on an unreliable behavior. Does this reading comport with the wisdom of the Monks? (cf my ref to ikegami above)

    So far, about the only reliable part of this node is the first line and the quote from perlop...


      Thanks for the correction, however, I free-typed the code in the original posting and errored in adding the commas to the qw() function strings. In my actual code, the while loop I gave an example of, works without the commas or when an array is created using a normal comma separated list.
Re: process array with while loop
by jdporter (Chancellor) on Nov 09, 2006 at 17:52 UTC

    Simply tie *FH, 'Tie::Handle::Array' => \@a;
    As in:

    my @a = qw( alpha beta gamma delta epsilon ); tie *FH, 'Tie::Handle::Array' => \@a; while (<FH>) { print "$_\n"; }

    Oh yeah — You'll need Tie::Handle::Array. The following is a minimal implementation:

    { package Tie::Handle::Array; sub TIEHANDLE { my( $pkg, $ar ) = @_; bless { A => $ar, I => 0, }, $pkg; } sub EOF { my $self = shift; $self->{I} > $#{ $self->{A} } } sub READLINE { my $self = shift; $self->EOF and return(); $self->{A}[ $self->{I}++ ] } }
    We're building the house of the future together.
Re: process array with while loop
by fenLisesi (Priest) on Nov 09, 2006 at 17:49 UTC
    use strict; use warnings; my @colors = qw(blue red orange brown); for my $color (@colors) { print "$color\n"; }
Re: process array with while loop
by jonadab (Parson) on Nov 09, 2006 at 18:50 UTC

    There is a case where you would use a while loop to process an array, but it's the situation wherein you are removing element(s) from the array as part of the processing and wish to continue as long as the array still has any contents:

    while (@array) { my $elem = pop @array; # or shift, to take from the front end # Do stuff with $elem here. }

    Sanity? Oh, yeah, I've got all kinds of sanity. In fact, I've developed whole new kinds of sanity. You can just call me "Mister Sanity". Why, I've got so much sanity it's driving me crazy.
Re: process array with while loop
by monsieur_champs (Curate) on Nov 09, 2006 at 17:38 UTC

    There are also other alternatives for foreach. My preferred one is map:

    @array = qw(blue red orange brown); map print, @array;

    map allows you to work loops as mathematical application (or functions), and this is the closest approach to my world model.

    UPDATE:Many thanks to ww, ikegami, merlyn for their comments and corrections.

      I was going to reply with "map in void context, for shame", but then I learned something!
        Except that print is not a void context. Therefore the map will create a list.
        #!/usr/bin/perl use strict; use warnings; my $a = ( print "Hello world\n" ); print "The answer is $a\n"; __END__
        See `perldoc -f print` for details.

      What advantage does this have over print @array;?

        No advantage, I just like this construct. It can even not be the more advisable, nor the one everybody uses, but it is quite readable for me and TIMTOWTDI is important. ;-)

Re: process array with while loop
by nedals (Deacon) on Nov 10, 2006 at 01:11 UTC
    A little off subject, but very useful for testing.
    while (<DATA>) { print $_; } __DATA__ blue red orange brown

Log In?

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://583157]
Approved by grep
Front-paged by GrandFather
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others taking refuge in the Monastery: (4)
As of 2023-05-30 18:25 GMT
Find Nodes?
    Voting Booth?

    No recent polls found