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

Dear Monks

My perl script makes use of an usual complicated array (or at least unusual for me) with all sorts of different elements.
Let's say the array is called @array which it starts getting populated not in index-numerical order thru the
push (@array,$element) instructions,
but rather thru the $array[$number]=$element where $number can take anyvalue between 1 and 120 and $element has an structure like this:

my $number=70 # I am just using 70 as an example my $element=[ $justanumber, # Just a number \%values, # This is hash previously populated ['',undef,[]], [], ]; $array[$number]=$element;
Now, lets say the element 70 of @array is at this moment the only one that has been established. Am I to assume that elements 0-69 and 71-120 are undefined?
Now, the scripts receives the instruction to evaluate an element of @array that has not yet been established. For example element 20.
sub asubroutine { $my number=shift ; # $number takes the value of 20 if ( not defined ($array[$number])) { print "There is no such element $number in the array.\n"; return; } my $x=$array[$number]; if (@{$x->[3]}>0) { # Do something } }
**************************** My problem is that not defined ($array[$number] returns false and the subroutine is not halted as it should because elment number 20 of @array was never declared/establish/define. Then when it comes to evaluate the line:
if (@{$x->[3]}>0) {
I get the error: Can't use an undefined value as an ARRAY reference at script.pl xxxx I overcome the problem by adding an extra conditional:
sub asubroutine { $my number=shift ; # $number takes the value of 20 if ( not defined ($array[$number])) { print "There is no such element $number in the array.\n"; return; } my $x=$array[$number]; if (defined $x->[1]) { if (@{$x->[3]}>0) { # Do something } } }
So, I don't think I had my problem fixed, but rather just patched. What I am looking for is a better acceptable solution based on facts and perhaps some enlightment into why the script was not working when it was suposed to. Why not defined ($array[$number]) returns false? If that particular element is not defined. This problem also hapens when an element of the array has been established/define at least once and later undef. I have tried
$array[$number] = undef,
I also tried:
delete $array[$number];
I even tried the splice function:
splice (@array,$number,1); #wipes out that element completely
followed by:
splice(@tables,$number,0,undef); #inserts a new undefined value in the + same element number
And still, same behaivior. My humble opinion is that due to the complexity of the element and perhaps some perl bug or limitation the element never gets wiped out or undefined completly, assuming I am allowed to say that besides 'undefined' and 'defined' the term 'kinda-undefined' or 'kinda-defined' are also a fact in perl. By the way, I am in Windows 7 32 bits running ActivePerl 5.10 Thanks in advance, matematiko

Replies are listed 'Best First'.
Re: Erroneous defined detection of array elements
by almut (Canon) on Nov 23, 2009 at 20:04 UTC

    You probably meant my $number=shift; (not $my number=shift; — though that would produce a different error message).

    Other than that, I don't see anything wrong with testing not defined ($array[$number]) to do what you want, and in fact it does work just fine for me... (in other words, I suppose the problem lies elsewhere).

    #!/usr/bin/perl use strict; use warnings; my @array; $array[70] = "foo"; asubroutine(20); sub asubroutine { my $number=shift ; # $number takes the value of 20 if ( not defined ($array[$number])) { print "There is no such element $number in the array.\n"; } } __END__ There is no such element 20 in the array.
Re: Erroneous defined detection of array elements
by GrandFather (Saint) on Nov 23, 2009 at 21:23 UTC

    How about starting with your code and a tiny sample input data set that demonstrates the issue. Then whittle the code down to a smallest possible test script that still demonstrates the problem. Then show us the result - if you've not solved the problem yourself in the process.

    It is very likely that there is an interaction going on between parts of your code that you haven't noticed. At some point in the whittling process your are almost certain to make the connection.


    True laziness is hard work
Re: Erroneous defined detection of array elements
by ikegami (Patriarch) on Nov 23, 2009 at 20:08 UTC

    Am I to assume that elements 0-69 and 71-120 are undefined?

    Yes. If they were undefined, they will remain undefined.

    $ perl -le' my @a; print 0+@a; print defined($a[0]) || 0; $a[70] = "a"; print 0+@a; print defined($a[0]) || 0; ' 0 0 elements in the array 0 and the first one isn't defined. 71 71 elements in the array 0 and the first one still isn't defined.

    My problem is that not defined ($array[$number] returns false

    Then $array[$number] is defined. It was given a value somewhere else, possibly through autovivification.

    $ perl -le' my @a; print defined($a[0]) || 0; $a[0][0]; print defined($a[0]) || 0; ' 0 1

    [@{$x->[3]}>0 gives] Can't use an undefined value as an ARRAY reference

    Then $array[$number][3] is not defined.

    Update: Added code blocks.

Re: Erroneous defined detection of array elements
by keszler (Priest) on Nov 23, 2009 at 20:13 UTC
    exists is a better test for hash and array elements, and will not auto-vivify them.
    perl -le ' $a[10] = 1; if (exists $a[2]) { print "how did that get there?\n"; } else { print "a[2] does not exist\n"; } ' a[2] does not exist

      That's bad advice. You should never have to use exists on an array element. It returns useless information and doesn't solve the problem you claims it solves (as explained by almut).

      See modules autovivification and warnings::autoviv. (Well, the latter doesn't exist yet. Working on it.)

      and will not auto-vivify them

      defined will not autovivify either:

      my @array; if (defined $array[20]) { } # no autovivific +ation, thus if (defined $array[20]) { warn "[20] does exist!" } # no warning

      Typically, it's a dereferencing operation that unexpededly autovivifies.  exists shares the same problem:

      my @array; if (exists $array[20]->[42]) { } if (exists $array[20]) { warn "[20] does exist!" } __END__ [20] does exist! at ./808911.pl line 5.
        I should have been more clear - defined does not distinguish between "never existed" and "is undef" for hash and array elements. I therefore consider exists a better test for unexpected auto-vivification.