6

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.

hexcoder
  • 1,218
  • 8
  • 13
  • 1
    You mean how to do a direct assignment to an array, using `[]` for anonymous array, having values inside the array that return false when tested with `exists`? – TLP Jul 03 '11 at 22:23
  • Yes. Being lazy, it would be nice to be able to specify this in one statement. Otherwise I have to use 'undef' or do it in multiple statements. – hexcoder Jul 03 '11 at 23:01

4 Answers4

6
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."
DavidO
  • 13,812
  • 3
  • 38
  • 66
  • Thanks a lot! Great answer for my 2nd question! In the meantime I found out that `delete $aref->[2];` changes back to 'not initialized' like it does for hash entries. – hexcoder Jul 03 '11 at 23:06
  • I'm glad you found it helpful. It's always fun working through these puzzles. – DavidO Jul 04 '11 at 05:31
4

That should do:

$a=[];
$#$a=4;

Update (replying to @hexcoder): In one statement:

$#{$a=[]}=4

And in one statement that returns the array:

$a = (map(($_,$#$_=4),[]))[0]

Though, not that I recommend using that construction...

salva
  • 9,943
  • 4
  • 29
  • 57
  • 1
    I agree, similarly `$a=[]; $a->[2] = 0;` should do also. But is it possible in one statement? Probably not without any support from a module. – hexcoder Jul 03 '11 at 22:38
1

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

You can check if the index is past the end. Beyond that, there's not much you can do.

$x = [];
undef $x->[9999];
print scalar @$x;

prints 10000. The undef $x->[9999] is equivalent to $x->[9999] = undef; Because none of the elements 0 to 9998 exist, perl will magically assign all of the intervening elements to undef.

David Hammen
  • 32,454
  • 9
  • 60
  • 108
  • Thanks. I do not agree with `perl will magically assign all of the intervening elements to undef`. As you can see in my first debugging output holes shows as 'empty slot'. If they were __assigned__ 'undef', the debugger would show 'undef' here. I have tried that, but did not show this in the question. – hexcoder Jul 03 '11 at 22:46
  • @hexcoder: That's because though their value is not defined, they do not have undef as a value. Strange nuance. – DavidO Jul 03 '11 at 23:16
  • @DavidO: Yep. in the debugger there is a difference between `x @x` and `x \@x`. The first will show 'undef', the second 'empty slot'. And `p @x` will give warnings for uninitialized values. – hexcoder Jul 03 '11 at 23:19
-1

You can only do that kind of thing from XS code (see for example Devel::Peek). Some, but not all, of it is exposed by the *::Util packages. (I've been working on a debugging/tracing package, so I know more about this than anyone should need to....)

geekosaur
  • 59,309
  • 11
  • 123
  • 114
  • Thanks for the pointers. I will have a look. Too bad, there is no direct Perl way (and i thought there is more than one way to do it ;-). – hexcoder Jul 03 '11 at 22:20
  • I could not find anything useful in *::Util. I hoped for something in List::*Util* or Array::Utils, but nothing. Could you be more specific? – hexcoder Jul 03 '11 at 22:57