Search code examples
perlanonymous-arrays

How to create an anonymous array ([]) with 'empty slots'?


I can create an array with 'empty slots' in it:

$ perl -wde 1
...
  DB<1> $x[2] = 0
  DB<2> x \@x
0  ARRAY(0x103d5768)
   0  empty slot
   1  empty slot
   2  0

or

  DB<3> $#y = 4
  DB<4> x \@y  
0  ARRAY(0x103d5718)
   0  empty slot
   1  empty slot
   2  empty slot
   3  empty slot
   4  empty slot

Please note: this is not the same as assigning undef.

But how do I specify that for an anonymous array using [ and ]?

This will not work:

  DB<5> x [,,0]
syntax error at (eval 27)[/usr/local/lib/perl5/5.10.0/perl5db.pl:638] line 2, near "[,"

And this fails too, since I only get the assigned value:

  DB<6> x []->[2] = 0
0  0

Bonus question: how can I check for an 'empty array slot' in my Perl script?

Background: In my test scripts I would like to be able to compare array contents precisely. For example I want to distinguish between 'not assigned' and 'assigned with an undef value'.

Thanks for any insights.


Solution

  • use feature qw/ say /;
    use strict;
    use warnings;
    
    my $aref;
    
    $#{$aref} = 4;
    $aref->[2] = undef;
    $aref->[3] = '';
    
    foreach my $idx ( 0 .. $#{$aref} ) {
        say "Testing $idx.";
        say "\t$idx exists." if exists $aref->[$idx];
        say "\t$idx defined." if defined $aref->[$idx];
    }
    
    OUTPUT:
    Testing 0.
    Testing 1.
    Testing 2.
        2 exists.
    Testing 3.
        3 exists.
        3 defined.
    Testing 4.
    

    We pre-allocated five spots in the anonymous array, @{$aref}. The top index is 4. We are able to find what the top index is the same way we created it; by testing the value of $#{$aref}. We can test for existence. We know everything between 0 and 4 was created. But Perl only reports "exists" for array elements that have specifically had something assigned to them (even if it's undef). Therefore, $aref->[2] is reported to exist, but isn't defined. Just for fun, we assigned '' to $aref->[3] to see a test report defined once. But the short story is that even though the array is pre-extended, we can still test for the difference between an element being initialized with undef, and an element being undef through array pre-extension, by using 'exists'.

    I can't say that's documented behavior of exists. So there's no guarantee it wouldn't change someday. But it works on 5.8, 5.10, 5.12, and 5.14.

    So, looking for a simple way to find which elements were initialized, which were defined, and which were not, here's an example:

    use feature qw/ say /;
    use strict;
    use warnings;
    
    my $aref;
    
    $#{$aref} = 4;
    $aref->[2] = undef;
    $aref->[3] = '';
    
    my @initialized = grep { exists $aref->[$_] } 0 .. $#{$aref};
    my @defined = grep { defined $aref->[$_] } 0 .. $#{$aref};
    my @uninitialized = grep { not exists $aref->[$_] } 0 .. $#{$aref};
    my @init_undef = grep { exists $aref->[$_] and not defined $aref->[$_] } 0 .. $#{$aref};
    say "Top index is $#{$aref}.";
    say "These elements are initialized: @initialized.";
    say "These elements are not initialized: @uninitialized.";
    say "These elements were initialized with 'undef': @init_undef.";
    say "These elements are defined: @defined."