http://www.perlmonks.org?node_id=160710

So, here comes a quick trivia question: What shall this little script output?
my @array = undef; if(@array) { print "defined\n"; } else { print "undefined\n"; }
To my own surprise, the output was this:
defined
I couldn't believe what I saw at first. I just spotted a similar 'bug' in my code where one of my subroutines would return 'undef' in an array assignment statement. My consequent if checks on that array thus failed (at least didn't work as I thought they should).

The trick here is that setting @array to undef doesn't really destroy or 'undefine' that array variable. It instead sets the first element of the array to 'undef'! Therfore, the array _does_ exist or rather it's size is positive (>0), albeit it's first item is 'undef'. At least that's the only way I can manage to explain this wierd behavior.

Setting array to '()' (empty array) on the other hand makes things work:
my @array = (); if(@array) { print "defined\n"; } else { print "undefined\n"; }


"There is no system but GNU, and Linux is one of its kernels." -- Confession of Faith

Replies are listed 'Best First'.
Re: undef'ing @arrays caveat
by Kanji (Parson) on Apr 20, 2002 at 00:40 UTC

    Since undef is a perfectly valid value for an element of an array, what your doing is no different than @array = ( undef ); sans the parens.

    Also, while @array = () may do what you want, @array is still defined; it just evaluates as false when you test for truth (which you were).

    To truly undefine @array you'd want to use undef @array; instead.

        --k.


      Actually, arrays cannot be undefined, just made empty (same with hashes). If you try undef @array in the debugger and then dump its value using the 'x' command, the effect is the same as if you had said @array = (), namely 'empty array'. (The same may be said for hashes, which under the above circumstances, produce the message, 'empty hash').

      Only scalars may be truly undefined; undef is itself merely a value, albeit a special one. As a C/C++ programmer, I rather think of scalars analagous to pointers -- sort of "auto-allocating, auto-dereferencing, type-ambivalent pointers", but pointers nonetheless, with undef rather analagous to NULL.

      dmm

      If you GIVE a man a fish you feed him for a day
      But,
      TEACH him to fish and you feed him for a lifetime

        I was thinking of definedness in the defined sense, but after some testing I discovered something really bizarre:

        Calling defined on an array that was declared as empty versus one that had been populated and then later emptied returns different results!

Re: undef'ing @arrays caveat
by Juerd (Abbot) on Apr 20, 2002 at 09:52 UTC

    You can only assign lists to arrays, but what you probably forgot is that not all lists need parentheses. So effectively, this happens:

    @array = (undef);
    It makes your array be a single element array with undef as its only value. To empty an array, use an empty list (because there is no scalar this time, you need the parens):
    @array = ();
    To really destroy the array, and also reset its MAX, use:
    undef @array;
    See also: undef.

    - Yes, I reinvent wheels.
    - Spam: Visit eurotraQ.
    

Re: undef'ing @arrays caveat
by lestrrat (Deacon) on Apr 20, 2002 at 01:28 UTC
    The trick here is that setting @array to undef doesn't really destroy or 'undefine' that array variable.

    When you think about it though, why would an assignment undefine the value of an array? When you're doing assignment, you're doing exactly that: assignment ;)

    But this following which your post inspired me to try out is, I gotta say, weird:

    perl -MData::Dumper -e '%hash="foo"; print Dumper( \%hash )' # produces this... ( so far so good... ) # $VAR1 = { # 'foo' => undef # }; perl -MData::Dumper -e '%hash = undef; print Dumper( \%hash )' # produces: # $VAR1 = { # '' => undef # };
      i don't find that so strange weird. in each case, the assignment to hash assumed a second argument of undef. it's as if you assigned %hash=undef,undef;. perl just helps you do what you mean. as in the post above, list context is assumed due to the type of the lvalue of the assignment operation.

      however, %hash=undef=>undef; may not do what you mean, if you don't know what you mean. here, the difference between , and => is plain.

      # $VAR1 = { # 'undef' => undef # };

      p.s. assignment is pretty much guaranteed to do just that--assign something. if you want to assign an empty hash, you'll have to assign one %hash = ()

      ~Particle ;Þ

        Oh I see, the '' is the stringified undef. Yes, that I appreciate, but

        however, %hash=undef=>undef; may not do what you mean, if you don't know what you mean. here, the difference between , and => is plain.

        Boy, did I sound *that* clueless? I was just verifying the various somevariable = undef behavior before I post an answer, and just came upon one that seemed weird strange. ugh.

Re: undef'ing @arrays caveat
by Maclir (Curate) on Apr 20, 2002 at 01:21 UTC
    What you need to determine first is "what does it mean for an array to be undefined?" If we takes as our definition that an array with no elements is undefined, then the standard (and self evident) way is to assign an empty array to it: @array = ();.

      True, and in fact if you undef @array the effect is identical. See my node, above.

        True, and in fact if you undef @array the effect is identical.

        Try using Devel::Peek. undef @array sets MAX to -1, @array = () does not. Or use Benchmark and find out that undef @array is about twice as fast. The effect is the same, but internally, there's something entirely different going on.

        - Yes, I reinvent wheels.
        - Spam: Visit eurotraQ.
        

Re: undef'ing @arrays caveat
by danger (Priest) on Apr 20, 2002 at 16:30 UTC
    I couldn't believe what I saw at first. I just spotted a similar 'bug' in my code where one of my subroutines would return 'undef' in an array assignment statement.

    This is why it is generally recommended that subroutines should use return with no arguments when the intent is to return a false value --- in scalar context it returns undef, in list context it returns an empty list.

      This is why it is generally recommended that subroutines should use return with no arguments when the intent is to return a false value --- in scalar context it returns undef, in list context it returns an empty list.

      It doe not return undef in scalar context, it still returns the empty list, but that will evaluate to undef. If a sub always returns a single element, it makes no sense to use return without arguments, and return undef is much more explicitly self documenting.

      If your sub normally returns a scalar, use an explicit return undef;.
      If your sub normally returns a list with more than one element, use an explicit return ();.
      If your sub is polymorphic, return without arguments. Or do so explicitly: return wantarray ? () : undef;.

      - Yes, I reinvent wheels.
      - Spam: Visit eurotraQ.
      

        It doe not return undef in scalar context, it still returns the empty list, but that will evaluate to undef.

        I don't think this kind of distinction is particularly useful --- the docs clearly state that in scalar context a bare return returns undef ... whether that is because the empty list evaluates to undef in scalar context or that an undef is explicitly returned is not relevant to this discussion.

        Using return without args just means: return wantwarray()? () : undef; and if that is the behaviour you desire, using a bare return is clear, concise, well documented, and convenient.

        If your sub normally returns a list with more than one element, use an explicit return ();.

        I don't see how that is more explicit. Both return; and return (); are exactly the same --- calling return with no arguments --- and they both do the context sensitive polymorhpic return (undef or empty list). Your way does not seem to me to be more explicitly passing an empty list any more than print (); vs print; is more explicitly passing an empty list to the print function.

Re: undef'ing @arrays caveat
by vladb (Vicar) on Apr 20, 2002 at 21:56 UTC
    Actually Juerd is right as far as my code goes.. In one of my scripts i have a subroutine which at the end does this:
    sub a_sub { my @things; . . . return ($should_return_stuff) ? @things : (); }
    In previous version of the code, I overlooked this part and had 'undef' in place of the pair of brackets '()' (for empty array). Since I have to do this conditional in the return statement, I can't simply have a return; statement all by itself (well, i could... but not without having to add an extra line of code). I somehow assumed that returning 'undef' would make things work. Certainly, I'm a new man now.. clean of the indispicable sin of the past *grin*.

    Thanks you all for good points/comments. My initial post was in fact meant to dispose the kind of stupidity that other monks (ak'a myself) get into from time to time. Also, I hoped the post could help a rare few monks who could otherwise fall in the same trap that I did... no big issue, though. ;-)

    "There is no system but GNU, and Linux is one of its kernels." -- Confession of Faith
Re: undef'ing @arrays caveat
by ariels (Curate) on Apr 21, 2002 at 10:55 UTC
    You might want to read very carefully the docs for defined: definedness is not a very useful properties for aggregates (= hashes and arrays). I guess the behaviour of undef on them is just the continuation of this.