Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number

Arrays are not lists

by tilly (Archbishop)
on Aug 05, 2000 at 09:52 UTC ( [id://26319] : perlmeditation . print w/replies, xml ) Need Help??

This meditation attempts to clarify a fundamental point that many monks have been led astray on. Compare the following two functions:
sub ret_array { return @_; } sub ret_list { return @_[0..$#_]; }
(EDIT: Alkabo pointed out why you shouldn't post code past midnight without re-reading and testing it. I just fixed an obvious typo in ret_list.)

Don't they look similar? Both just return what they were passed, don't they?

But look again. They differ in one significant particular. ret_list returns a list of things. ret_array an array.

But isn't an array a list?

As merlyn put it to me once, a list is a list of things and an array is a bag that contains a list. Now all things in Perl obey a context, which is scalar or list. In list context a list gives you itself. In scalar context it gives the last element. In list context an array gives you the list in the bag. In scalar context it gives you an envelope from the bag, the label saying how many things are in it.

These are seldom the same, and many is the time that a function which was supposed to return a list of words would return a 1 to me unexpectedly when I expected one word.

The moral is this. Think carefully when you write a function that returns a list of things. Some day it will be called in scalar context. Think about what you want it to do when that hapens, then test wantarray() before your return.

Replies are listed 'Best First'.
(Slice 'em and Dice 'em) RE: Arrays are not lists
by mwp (Hermit) on Aug 05, 2000 at 12:25 UTC
    sub ret_list { return $_[0..$#_]; }

    I'd like to give you a good trouting, but I'm not certain if you did this on purpose or not. :-)

    The "proper" way to take a slice is as follows:

    sub ret_slice { return @_[0..$#_]; }

    This will return the desired result. I think a better example to illustrate your point (lists vs. arrays) would have been this:

    # the data our @zot = qw(apples Ford perl Jennifer); # the output print "Func Context RetVal \n", "---- ------- ------ \n"; { # our function my @list = &ret_std( @zot ); my $scalar = &ret_std( @zot ); print "Std LIST @{list} \n", # prints 'apples Ford perl' "Std SCALAR ${scalar} \n\n"; # prints 3 } { # a poorly-written function my @list = &ret_bad( @zot ); my $scalar = &ret_bad( @zot ); print "Bad LIST @{list} \n", # prints 'apples Ford perl' "Bad SCALAR ${scalar} \n\n"; # prints 'perl' } { # a better function my @list = &ret_good( @zot ); my $scalar = &ret_good( @zot ); print "Good LIST @{list} \n", # prints 'apples Ford perl' "Good SCALAR ${scalar} \n\n"; # prints 'apples Ford perl' } # the functions # returns full list, or number of elements sub ret_std { my @foo = @_[0..2]; return @foo; } # returns a list each time, but how long, and which parts?? sub ret_bad { return @_[0..2]; } # the "proper" function (from perldoc -f wantarray) # returns the full list, as a space-delimited scalar or list sub ret_good { my @bar = @_[0..2]; return (wantarray()) ? @bar : "@bar"; }

    I apologize for the length and relative messiness of the code (this would be a good place to use write formats) but I hope I get my point across. Essentially, I follow what you're saying and you raise several crucial issues. Most importantly, PAY ATTENTION to A) where the return value(s) from your function are being used, and B) how your function is delivering those return values. Is it clear, or at least documented? &ret_bad() in particular scares me, I would hate to have a library full of functions like that. "Huh, I got the LAST element of the list? WTF?"

    I hope I don't come across as being snide. I understand what you are trying to say and it was definitely a very thoughtful post, and should serve as a warning to us all. Thank you. :-) Patience, meditation, and good use of the scalar function will see us through.


      Another approach is to be specific. If you want to return a list of items, and your invocation makes no sense in a scalar context, say so:
      sub this_returns_a_list_only { wantarray or die "DURN IT, USE ME IN A LIST CONTEXT!"; ... ... }
      If you code like that, you can safely add all the return expressions you want, knowing they will be always invoked in a list context.

      There is no "proper" relationship between what a function returns in a list context and what it returns in a scalar context. There's only the relationship that makes sense for the domain in which the function returns.

      For example, it makes perfect sense to me that sortalways returns undef in a scalar context. What one single value would make sense to be returned otherwise? And that wonderful getpwnam, which returns a 9-element list in a list context, or just the most useful part of that (the second element, the user id number) in a scalar context.

      And finally, the localtime,gmtime twins. Very cool that the scalar context definition is completely unrelated to the list context definition.

      So, perhaps you can stop thinking of "what one expression makes sense to return" and think instead of "what do I want this subroutine to return in a list context vs. a scalar context?" and write your code appropriately. That's why wantarray is in there!

      -- Randal L. Schwartz, Perl hacker

      D'oh. Thanks for pointing out my obvious typo. That is what I get for posting late at night without testing my code.

      What I consider the most important point here is not that lists and arrays differ. Rather it is that there are a lot of ways in which scalar and array context can have unexpected behaviour, and there are a lot of hidden mines awaiting the person who does not think about which is which.

      Being aware of the context and making a point of consistently exploring it will teach you a lot of nuances of code.

      BTW I am no longer bitten by the difference between lists and arrays. There was a period when I realized that Perl really is list-oriented and I tried to make my code be list-oriented and I got bitten during the transitition...

RE: Arrays are not lists
by reptile (Monk) on Aug 05, 2000 at 11:47 UTC

    That reminds me: check out Damian Conway's proposal to replace wantarray with a generic want function to test against every possible context you could imagine. I hope this isn't too offtopic :p

    local $_ = "0A72656B636148206C72655020726568746F6E41207473754A"; while(s/..$//) { print chr(hex($&)) }

RE: Arrays are not lists
by Anonymous Monk on Aug 05, 2000 at 10:37 UTC
    Wow.... How did I grind away at Perl for weeks, and read more than one excellent book, without realizing that arrays are not lists? I've fixed several errors caused by this misconception without figuring out why the original code failed. This explanation will go a long way toward getting me off my blood pressure medication.
      Perl actually stands for Pathologically Eclectic Rubbish Lister, but don't tell anyone I said that.

      -- (BUGS section of "perldoc perl")

      Here is my theory.

      Perl is a list-oriented language. Widely known and (un)popular languages like VB, Pascal, C (and derivatives) generally are not. Therefore the list-oriented features of Perl tend to be downplayed by both authors and programmers, leading to much confusion.

      Make sense?