Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Two nested while loops on same array become infinite (they shouldn't)

Tags:

perl

I have a minimal working example of two nested while loops on the same array @g:

#!/usr/bin/env perl

use 5.042;
no source::encoding;
use warnings FATAL => 'all';
use feature 'say';
use autodie ':default';

my @g = qw(Matthew Mark Luke John);
while (my ($i, $gos) = each @g) {
    say "$i => $gos";
    while (my ($j, $g2) = each @g) {
        say "\t$j=>$g2";
    }
}

This particular loop gets stuck on the first iteration $i, and never moves on to the 2nd.

The infinity is easily fixed by foreach my $gos (@gos) { instead of the first while.

Is this a bug or an intended feature?

Shouldn't foreach my $gos (@g) in the first line be the exact equivalent of while (my ($i, $gos) = each @g) {?

like image 245
con Avatar asked Oct 30 '25 11:10

con


2 Answers

Since you are using v5.42, you should use foreach and indexed which is designed to do just this:

use 5.042;
$|=1;

my @g = qw(Matthew Mark Luke John);

foreach my($i, $gos) ( indexed @g ) {
    say "$i => $gos";

    foreach my($j, $g2) ( indexed @g ) {
        say "\t$j=>$g2";
        }
    }
like image 176
brian d foy Avatar answered Nov 02 '25 11:11

brian d foy


The problem is that both invocations of each use the same list iterator. From the perldoc:

Each hash or array has its own internal iterator, accessed by each, keys, and values. The iterator is implicitly reset when each has reached the end [...]

After returning all elements, the iterator returns an empty list in the inner loop, upon which it restarts from the beginning. This is why your outer loop never progresses beyond the first element: all remaining elements are consumed by the inner loop on every iteration.

The repeated output you're seeing clearly demonstrates the shared iterator behavior:

0=>Matthew     # Consumed by outer loop
    1=>Mark    # Consumed by inner loop
    2=>Luke    # Consumed by inner loop
    3=>John    # Consumed by inner loop
    ()         # Iterator is exhausted and reset. Loop exits
0=>Matthew     # A fresh iterator
    1=>Mark
    2=>Luke
    3=>John
    ()         # Iterator is exhausted and reset. Loop exits
# ... and so on

You should use foreach instead.

like image 32
Robby Cornelissen Avatar answered Nov 02 '25 13:11

Robby Cornelissen



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!