Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much
 
PerlMonks  

For- loop increment not passing to inner block

by Anonymous Monk
on Mar 30, 2008 at 19:23 UTC ( [id://677372]=perlquestion: print w/replies, xml ) Need Help??

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

Most esteemed monks, I suspect I'm missing something really obvious here, but I'm in a quandary. I have a while loop nested inside a for loop, like so:
for (my $i = 0; $i <= 4; $i++) { while (my $line = readline(SORTD)) { my $number = substr($line, $i*2, 2); } }
The problem: the value of i increments fine in the outer block, but the inner block simply receives the initial value of i (0 or otherwise), and never increments. It seems a no-brainer scoping issue, but I expected the for-loop increment to be passed to the inner block. Obviously, that's not happening. What am I missing here?

Replies are listed 'Best First'.
Re: For- loop increment not passing to inner block
by CountZero (Bishop) on Mar 30, 2008 at 20:16 UTC
    There are two problems with your code:
    1. Assuming SORTD is some form of file-handle, the first time you arrive at the while-loop ($i being equal to 0), the while-loop will totally exhaust the filehandle. All following calls to readline(SORTD) (when $i is incremented and the while-loop is entered again, will just return undef and the while-loop will not even start. Re-entering the loop does not reset the filehandle to the beginning of the file.
    2. The value of $i remains constant throughout each iteration of the for loop. So the while-loop will see the same value of $i for the whole of the file you are reading.
    One more comment: it is more customary to write readline(SORTD) as <SORTD>

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

      Ah! Of course! I wasn't resetting the filehandle! (Must. . . have. . . coffee. . ) The filehandle SORTD refers a textfile like this:
      0912203749 0725284648 0608294149 0622424347 1219303436 1729313449 1015162331 . . .
      These are actually groupings of five 2-digit numbers. My goal was to traverse it by column, using i*2 as a starting index (that part, at least, works fine). I probably should have explained that at the outset but, silly me, I thought the issue was probably too simple to require further elaboration (I won't make that mistake again). Bountiful thanks, CountZero!
        I might not understand the overall task, but I would be inclined to invert the order of your two loops: the "while" loop over the file data should be the outer loop, and the "for" loop over columns in each line should be the inner loop.

        If the file in question is not very big (e.g. less than, say, 50 MB on typical desktop machines of the last few years), and you have some reason to prefer your current ordering of the loops, it would be better to read the whole file content into a memory-resident array first, then use a nested "for" loop over that array, rather than repeating your "while" loop over the file contents.

        The point is that file i/o is quite slow relative to memory-internal operations, so reading data from a file just once (rather than five times) just makes more sense.

        This is bread and butter stuff for Perl and there are many better ways to achieve what you are after. Some are likely to be outside your current comfort zone so sift through the following and see what you like the look of:

        use warnings; use strict; while (<DATA>) { chomp; my @numbers = unpack ("(a2)*", $_); print "unpacked: @numbers, "; @numbers = /(..)/g; print "matched: @numbers, "; @numbers = (); push @numbers, substr $_, 0, 2, '' while length $_; print "substr'd: @numbers\n"; } __DATA__ 0912203749 0725284648 0608294149 0622424347 1219303436 1729313449 1015162331

        Prints:

        unpacked: 09 12 20 37 49, matched: 09 12 20 37 49, substr'd: 09 12 20 +37 49 unpacked: 07 25 28 46 48, matched: 07 25 28 46 48, substr'd: 07 25 28 +46 48 unpacked: 06 08 29 41 49, matched: 06 08 29 41 49, substr'd: 06 08 29 +41 49 unpacked: 06 22 42 43 47, matched: 06 22 42 43 47, substr'd: 06 22 42 +43 47 unpacked: 12 19 30 34 36, matched: 12 19 30 34 36, substr'd: 12 19 30 +34 36 unpacked: 17 29 31 34 49, matched: 17 29 31 34 49, substr'd: 17 29 31 +34 49 unpacked: 10 15 16 23 31, matched: 10 15 16 23 31, substr'd: 10 15 16 +23 31

        Perl is environmentally friendly - it saves trees
        Thanks for the tips all, particularly Grandfather. I can assure you that, to the extent I have a "comfort zone," it's simply habits and prejudices carrying over from other languages I've used. There may be more than one way to do it, but I have no interest in using a Stradavarius to pound nails. I'm always interested in a better way to do it, and if that requires learning new things, that's all the better. Thanks again!
Re: For- loop increment not passing to inner block
by FunkyMonk (Chancellor) on Mar 30, 2008 at 21:42 UTC
    Since nobody's mentioned it yet
    for (my $i = 0; $i <= 4; $i++) {

    is more idiomatically written

    for my $i ( 0 .. 4 ) {

      Not only more Perlish (idiomatically), but shorter, clearer, safer and more maintainable - what's not to like?.


      Perl is environmentally friendly - it saves trees
Re: For- loop increment not passing to inner block
by pc88mxer (Vicar) on Mar 30, 2008 at 19:27 UTC
    By 'inner block' do you mean this:
    while (my $line = readline(SORTD)) { my $number = substr($line, $i*2, 2); }
    If so, I don't see where you are incrementing $i.

    The way the for loop works is that $i will not be incremented until the end of the for block is encountered, so $i is not going to be incremented inside the while loop unless you explicitly do it yourself.

Re: For- loop increment not passing to inner block
by igelkott (Priest) on Mar 30, 2008 at 20:02 UTC

    Could you possibly have meant:

    for (my $i = 0; $i <= 4; $i++) { my $j=0; while (my $line = readline(SORTD)) { $j++; my $number = substr($line, $j*2, 2); } }

    Just a wild guess of course but when you wanted the counter of the inner loop to vary, you probably meant to have a different variable.

    (There are nicer ways of writing this but I wanting the changes to be obvious.)

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others having a coffee break in the Monastery: (4)
As of 2024-03-29 05:27 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found