perl-diddler has asked for the wisdom of the Perl Monks concerning the following question:

I'm not quite sure I understand this perl behavior.
my @bar=undef; print "def=",defined(@bar), " l=", $#bar, "\n";' output: def=1 l=0
This is confusing me. I had a function that returned an array of answers if it found one or more answers, else it returned undef. On return, the result was assigned to an array and I tested the array for being defined or not.

I thought that if my function returned undef and that was assigned to an array, the array should also test as not defined. Buuuut....noooooOOOOoooo.... :-(

Why does my array, assigned "undef", then tell me it is defined? :-(... Very sad. I thought undef was the opposite of defined (sort of). I mean "undef" is a value (or an "unvalue"?), and defined is a test to see if a 'value' doesn't evaluate to 'undef', no? I'm sure there's a good reason for this, but at this point it sure doesn't seem "intuitive"...Arrg....

(this is what I get for doing whatever it is that I've done to deserve this! :-))....

Replies are listed 'Best First'.
Re: undef==defined sometimes? (body question)
by ikegami (Patriarch) on May 02, 2008 at 23:16 UTC

    defined is not meant to be used on an array. For starters, it would be impossible for an array to be undefined since undef is a scalar value.

    As such, it appears that defined imposes a scalar context on its argument. An array name in scalar context is the number of elements in that array. Numbers are defined, so defined(@bar) always returns true.

    Furthermore, my @bar = undef; doesn't create an empty array. It creates an array with one element in it (undef). my @bar = (); and my @bar; are equivalent and create empty arrays.

    If you want to check if the array is empty, just use the array name in scalar context:

    my @bar = undef; print("There are ", scalar(@bar), " elements in \@bar\n"); print("\@bar is empty\n") if !@bar; print("\@bar isn't empty\n") if @bar; my @foo; print("There are ", scalar(@foo), " elements in \@foo\n"); print("\@foo is empty\n") if !@foo; print("\@foo isn't empty\n") if @foo;
    There are 1 elements in @bar @bar isn't empty There are 0 elements in @foo @foo is empty

    You can check if an *array element* is defined, since array elements are scalars.

    my @bar = (undef, 'a'); print(defined($bar[0])?1:0, "\n"); # 0 print(defined($bar[1])?1:0, "\n"); # 1 print(defined($bar[2])?1:0, "\n"); # 0 (doesn't exist)

    Update: Clarified some details
    Update: "Deleted" a paragraph in response to ysth's reply.

      As such, it appears that defined imposes a scalar context on its argument. An array name in scalar context is the number of elements in that array. Numbers are defined, so defined(@bar) always returns true.
      Nope. defined() on arrays or hashes is special; it returns whether they've ever had storage allocated for elements. But because this has historically been misused (as in the OPs case), this behavior was deprecated in 5.6.0, and Linda should have seen a deprecated warning. See defined.
      So, if I have a function that returns undef on error, else an array with results, I should assign it to a scalar and and see if it is undef, if not, then hope it's a reference to an array?

      If I'm "in" the function, and I return an array to a scalar won't perl coerce the array into a scalar length and assign that to my scaler instead? So I'll end up with either the number of elements in the array, or 'unef', but not the array?

      Seems like this is pushing me in the direction of not being able to return an array with a list of found values or 'undef' ... I think I 'resent' the syntax that @a=1, is an assignment to $a[0]...I can understand why it happens, but I'm not sure how useful it is given that by using the "@" and no index, I'm only wanting an array to be assigned. It seems a perversity to coerce undef into a defined array with value undef in the first element....

      Under what situations is this commonly useful? Or are there too many to number? Sigh. ...

      If I want to return 'undef' and have it be interpreted as an undef array, then I need to return ()....a bit of a pain, since I wanted higher level routines to not care about the type of a return unless they needed to use it, with undef always being 'undef'.....

      Just seems to complicate matters a bit.

        Seems like this is pushing me in the direction of not being able to return an array with a list of found values or 'undef' ...
        Right, because it's perfectly valid to return a list with the single value 'undef', and that's indistinguishable from a sub returning 'undef' as meaning 'nothing to return'.
        I think I 'resent' the syntax that @a=1, is an assignment to $a[0]...I can understand why it happens, but I'm not sure how useful it is given that by using the "@" and no index, I'm only wanting an array to be assigned. It seems a perversity to coerce undef into a defined array with value undef in the first element...
        @a=1 is indeed an array operation; it clears any current content in the whole array before assigning to $a[0].

        You'd suffer less confusion if you ditch the idea of "array" being a type of value. Values in perl are either lists or scalars; "array" is a variable type that can store a list (as is "hash", but in a different way).

        I think I 'resent' the syntax that @a=1, is an assignment to $a[0]...

        Why would you resent that and not resent that @a = (1, 2, 3) is an assignment to $a[0], $a[1], and $a[2], respectively? (Don't make the mistake that the parentheses have anything to do with list creation; they only exist for grouping, as the precedence of the assignment operator is higher than that of the comma operator.)

        So, if I have a function that returns undef on error, else an array with results, I should assign it to a scalar and and see if it is undef, if not, then hope it's a reference to an array?

        Functions can *only* return lists of scalars. That list can contain 0, 1 or more scalars. One way to address your problem is to return an empty list (a list with no elements) on error, but that only works if returning an empty list never occurs on success.

        sub func { my ($error) = @_; my @results = ('some', 'values'); # 1 or more values. $error ? () : @results; } for (0..1) { my @results = func($_); print(@results ? "no error (@results)" : 'error', "\n"); }

        Another way is to always return exactly one scalar: undef on error or an array reference on success.

        sub func { my ($error) = @_; my @results = (); # 0 or more values. $error ? undef : \@results; } for (0..1) { my $results = func($_); print($results ? "no error (@$results)" : 'error', "\n"); }

        Why don't you just return nothing at all on error, and get over the non-problem problem?

        There was really no point in writing code here in hindsight. So ...

Re: undef==defined sometimes? (title question)
by ikegami (Patriarch) on May 02, 2008 at 23:43 UTC

    To answer the question in your title (which is quite different from the question in your post), undef==$defined can indeed be true. It's true when $defined is zero when coerced into a number, since the numerical comparison operator (==) coerces its operands into numbers and undef is zero when coerced into a number.

    See my reply in this recent thread for more details.