in reply to Re^2: Using exists to check element in array
in thread Using exists to check element in array

and he asked for the rational.

Hasn't that been answered already?

It's because exists $a[i] doesn't really check if the element exists in the array. It's not sufficient for the element to be part of the array; the pointer in the array buffer must be something other than NULL.

For example,

my @a; $a[1] = undef; $a[2] = 0; $a[3] = 7;
Element of the arrayexistsdefinedTrue
$a[0]YesNoNoNo
$a[1]YesYesNoNo
$a[2]YesYesYesNo
$a[3]YesYesYesYes
$a[4]NoNoNoNo

It might also be warning of problems with using NULL as a special value. I suspect Perl is quite consistent at keeping NULL elements NULL, but there might be surprise cases (for (@a)? for ((), @a)? f(@a)?)

Update: Replaced some uses of "exists".

Replies are listed 'Best First'.
Re^4: Using exists to check element in array
by LanX (Saint) on Jan 29, 2024 at 19:54 UTC
    To misquote Bill Clinton:

    "It depends if your meaning of exist exists."

    ;)

    Your table shows exactly a consistent behavior to exists when used with hashes. Unset values just don't exist.

    $a[0] was never set, hence it doesn't exist.

    I wasn't sure what you and sectokia meant with null-pointer, apparently it's the C-value of unset "gap" values¹ in the underlying C-array.

    But those exist only on the C level, not logically in Perl.

    > but there might be surprise cases (for (@a)? for ((), @a)? f(@a)?)

    I don't understand, probably above my expertise.

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    see Wikisyntax for the Monastery

    ¹) with an index smaller than the maximal index of a set value

      Your table shows exactly a consistent behavior to exists when used with hashes.

      No, it's not consistent with hashes.

      $ perl -e' my %h = ( a => 4, c => 6 ); printf( "exists %s return false for elements found in a hash.\n", ( grep { !exists( $h{$_} ) } keys %h ) ? "can" : "didn\x27t" ); my @a; $a[0] = 4; $a[2] = 6; printf( "exists %s return false for elements found in an array.\n", ( grep { !exists( $a[$_] ) } keys @a ) ? "can" : "didn\x27t" ); ' exists didn't return false for elements found in a hash. exists can return false for elements found in an array.

      The very existence of this inconsistency is reason enough for a warning.

      $a[0] was never set, hence it doesn't exist.

      No, that means it wasn't set. Setting or not setting has nothing to do whether an element is found in an array or not, and with what exists returns.

      exists can obviously return false for elements that were never set, but it can also return true for such elements as the following demonstrates:

      $ perl -Mv5.14 -e'my @a; \$a[0]; say exists( $a[0] ) || 0;' 1

      Your implication that exists checks whether an element was set or not is incorrect. As I already mentioned, exists checks if an element is within the array and if it isn't NULL.

      I don't understand, probably above my expertise.

      These are things that might unintentionally change replace the NULL with a (pointer to a) scalar. I didn't test.

      Upd: I just did and they don't. But \$a[0] does, as shown above.

        Time and again I see you changing your posts in retrospect without marking updates.

        For the sake of a decent discussion, please be consistent.

        Now, I'll split my reply into multiple posts, so lets target my first argument:

        Firstly:

        your original example was:

        my @a; $a[1] = undef; $a[2] = 0; $a[3] = 7;

        and I replied

        > > Your table shows exactly a consistent behavior to exists when used with hashes. Unset values just don't exist.

        and here we go

        use v5.12; use warnings; my (@x,%x); my $i=1; for my $v (undef,0,7) { $x[$i]=$x{$i}=$v; $i++; }
        --- Index exists? $i @x %x 0 1 1 1 2 1 1 3 1 1 4

        QED!

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        see Wikisyntax for the Monastery

        Secondly

        > ( grep { !exists( $a[$_] ) } keys @a ) ? "can" : "didn\x27t" );

        Looks like keys @array was added to Perl in 5.12

        And it's obviously inconsistent to delete and exists .

        > The very existence of this inconsistency is reason enough for a warning.

        But it's even worse!!!

        keys is inconsistent to the behavior in hashes, even without using delete and exists and ever seeing any warnings !!!

        my (@y,%y); $y[3]=$y{3}=1; say "keys \%y: ", join ",",keys %y; say "keys \@y: ", join ",",keys @y;

        -->

        keys %y: 3 keys @y: 0,1,2,3

        A consistent implementation of keys ARRAY should check with exists first before returning an index.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        see Wikisyntax for the Monastery

        Thirdly:

        > exists can obviously return false for elements that were never set, but it can also return true for such elements as the following demonstrates:

        >

        $ perl -Mv5.14 -e'my @a; \$a[0]; say exists( $a[0] ) || 0;' 1

        now that's extra weird, because introspecting with Devel::Peek reveals that the implementation is (falsely) differentiating between different kinds of 0s and NULLs.

        I'm no C nor even XS hacker, but this looks like exists's understanding of NULL is just to narrow. ¹

        I can't see why this shouldn't be fixable.

        use Devel::Peek; my @z; $a = \$z[1]; say "\$z[$_] exists: ", exists( $z[$_] ) || 0 for 0..2; say '---Dump @z'; Dump @z; say '---Dump $z[0]'; Dump $z[0]; say '---Dump $z[1]'; Dump $z[1];

        comments ### added
        $z[0] exists: 0 $z[1] exists: 1 $z[2] exists: 0 ---Dump @z SV = PVAV(0x2770318) at 0x27a4930 REFCNT = 1 FLAGS = () ARRAY = 0x2787080 FILL = 1 MAX = 3 FLAGS = (REAL) Elt No. 0 SV = 0 ### 0 Elt No. 1 SV = NULL(0x0) at 0x2783d70 REFCNT = 2 FLAGS = () ---Dump $z[0] SV = NULL(0x0) at 0x7a91d0 ### NULL (WTF?) REFCNT = 2147483641 FLAGS = (READONLY,PROTECT) ---Dump $z[1] SV = NULL(0x0) at 0x2783d70 REFCNT = 2 FLAGS = ()

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        see Wikisyntax for the Monastery

        ¹) a null understanding ;-)