home | O'Reilly's CD bookshelfs | FreeBSD | Linux | Cisco | Cisco Exam  


Book Home Programming PerlSearch this book

2.8. List Values and Arrays

Now that we've talked about context, we can talk about list literals and how they behave in context. You've already seen some list literals. List literals are denoted by separating individual values by commas (and enclosing the list in parentheses where precedence requires it). Because it (almost) never hurts to use extra parentheses, the syntax diagram of a list value is usually indicated like this:

(LIST)
Earlier we said that LIST in a syntax description indicates something that supplies list context to its arguments, but a bare list literal itself is the one partial exception to that rule, in that it supplies a list context to its arguments only when the list as a whole is in list context. The value of a list literal in list context is just the values of the arguments in the order specified. As a fancy sort of term in an expression, a list literal merely pushes a series of temporary values onto Perl's stack, to be collected off the stack later by whatever operator wants the list.

In a scalar context, however, the list literal doesn't really behave like a LIST, in that it doesn't supply list context to its values. Instead, it merely evaluates each of its arguments in scalar context, and returns the value of the final element. That's because it's really just the C comma operator in disguise, which is a binary operator that always throws away the value on the left and returns the value on the right. In terms of what we discussed earlier, the left side of the comma operator really provides a void context. Because the comma operator is left associative, if you have a series of comma-separated values, you always end up with the last value because the final comma throws away whatever any previous commas produced. So, to contrast the two, the list assignment:

@stuff = ("one", "two", "three");
assigns the entire list value to array @stuff, but the scalar assignment:
$stuff = ("one", "two", "three");
assigns only the value "three" to variable $stuff. Like the @files array we mentioned earlier the comma operator knows whether it is in a scalar or list context, and chooses its behavior accordingly.

It bears repeating that a list value is different from an array. A real array variable also knows its context, and in a list context, it would return its internal list of values just like a list literal. But in a scalar context it returns only the length of the array. The following assigns to $stuff the value 3:

@stuff = ("one", "two", "three");
$stuff = @stuff;
If you expected it to get the value "three", you were probably making a false generalization by assuming that Perl uses the comma operator rule to throw away all but one of the temporary values that @stuff put on the stack. But that's not how it works. The @stuff array never put all its values on the stack. It never put any of its values on the stack, in fact. It only put one value, the length of the array, because it knew it was in scalar context. No term or operator in scalar context will ever put a list on the stack. Instead, it will put one scalar on the stack, whatever it feels like, which is unlikely to be the last value of the list it would have returned in list context, because the last value is not likely to be the most useful value in scalar context. Got that? (If not, you'd better reread this paragraph, because it's important.)

Now back to true LISTs, the ones that do list context. Until now we've pretended that list literals were just lists of literals. But just as a string literal might interpolate other substrings, a list literal can interpolate other sublists. Any expression that returns values may be used within a list. The values so used may be either scalar values or list values, but they all become part of the new list value because LISTs do automatic interpolation of sublists. That is, when a LIST is evaluated, each element of the list is evaluated in a list context, and the resulting list value is interpolated into LIST just as if each individual element were a member of LIST. Thus arrays lose their identity in a LIST.[18] The list:

(@stuff,@nonsense,funkshun())

[18]Some people seem to think this is a problem, but it's not. You can always interpolate a reference to an array if you do not want it to lose its identity. See Chapter 8, "References".

contains the elements of @stuff, followed by the elements of @nonsense, followed by whatever values the subroutine &funkshun decides to return when called in list context. Note that any or all of these might have interpolated a null (empty) list, in which case it's as if no array or function call had been interpolated at that point. The null list itself is represented by the literal (). As with a null array, which interpolates as a null list and is therefore effectively ignored, interpolating the null list into another list has no effect. Thus, ((),(),()) is equivalent to ().

A corollary to this rule is that you may place an optional comma at the end of any list value. This makes it easy to come back later and add more elements after the last one:

@releases = (
    "alpha",
    "beta",
    "gamma",
);

Or you can do away with the commas entirely: another way to specify a literal list is with the qw (quote words) syntax we mentioned earlier. This construct is equivalent to splitting a single-quoted string on whitespace. For example:

@froots = qw(
    apple       banana      carambola
    coconut     guava       kumquat
    mandarin    nectarine   peach
    pear        persimmon   plum
);
(Note that those parentheses are behaving as quote characters, not ordinary parentheses. We could just as easily have picked angle brackets or braces or slashes. But parens are pretty.)

A list value may also be subscripted like a normal array. You must put the list in parentheses (real ones) to avoid ambiguity. Though it's often used to fetch a single value out of a list, it's really a slice of the list, so the syntax is:

(LIST)[LIST]
Examples:
# Stat returns list value.
$modification_time = (stat($file))[9];

# SYNTAX ERROR HERE.
$modification_time = stat($file)[9];  # OOPS, FORGOT PARENS

# Find a hex digit.
$hexdigit = ('a','b','c','d','e','f')[$digit-10];

# A "reverse comma operator".
return (pop(@foo),pop(@foo))[0];

# Get multiple values as a slice.
($day, $month, $year) = (localtime)[3,4,5];

2.8.1. List Assignment

A list may be assigned to only if each element of the list is itself legal to assign to:

($a, $b, $c) = (1, 2, 3);

($map{red}, $map{green}, $map{blue}) = (0xff0000, 0x00ff00, 0x0000ff);
You may assign to undef in a list. This is useful for throwing away some of the return values of a function:
($dev, $ino, undef, undef, $uid, $gid) = stat($file);
The final list element may be an array or a hash:
($a, $b, @rest) = split;
my ($a, $b, %rest) = @arg_list;
You can actually put an array or hash anywhere in the list you assign to, but the first array or hash in the list will soak up all the remaining values, and anything after it will be set to the undefined value. This may be useful in a local or my, where you probably want the arrays initialized to be empty anyway.

You can even assign to the empty list:

() = funkshun();
That ends up calling your function in list context, but discarding the return values. If you had just called the function without an assignment, it would have instead been called in void context, which is a kind of scalar context, and might have caused the function to behave completely differently.

List assignment in scalar context returns the number of elements produced by the expression on the right side of the assignment:

$x = ( ($a, $b) = (7,7,7) );    # set $x to 3, not 2
$x = ( ($a, $b) = funk() );     # set $x to funk()'s return count
$x = ( () = funk() );           # also set $x to funk()'s return count
This is handy when you want to do a list assignment in a Boolean context, because most list functions return a null list when finished, which when assigned produces a 0, which is interpreted as false. Here's how you might use it in a while statement:
while (($login, $password) = getpwent) {
    if (crypt($login, $password) eq $password) {
        print "$login has an insecure password!\n";
    }
}

2.8.2. Array Length

You may find the number of elements in the array @days by evaluating @days in a scalar context, such as:

@days + 0;      # implicitly force @days into a scalar context
scalar(@days)   # explicitly force @days into a scalar context
Note that this only works for arrays. It does not work for list values in general. As we mentioned earlier, a comma-separated list evaluated in scalar context returns the last value, like the C comma operator. But because you almost never actually need to know the length of a list in Perl, this is not a problem.

Closely related to the scalar evaluation of @days is $#days. This will return the subscript of the last element of the array, or one less than the length, since there is (ordinarily) a 0th element. Assigning to $#days changes the length of the array. Shortening an array by this method destroys intervening values. You can gain some measure of efficiency by pre-extending an array that is going to get big. (You can also extend an array by assigning to an element beyond the end of the array.) You can truncate an array down to nothing by assigning the null list () to it. The following two statements are equivalent:

@whatever = ();
$#whatever = -1;
And the following is always true:
scalar(@whatever) == $#whatever + 1;
Truncating an array does not recover its memory. You have to undef(@whatever) to free its memory back to your process's memory pool. You probably can't free it all the way back to your system's memory pool, because few operating systems support this.



Library Navigation Links

Copyright © 2001 O'Reilly & Associates. All rights reserved.