Programming Perl, Second Edition

Previous Chapter 1 Next
 

1.8 List Processing

Much earlier in this chapter, we mentioned that Perl has two main contexts, scalar context (for dealing with singular things) and list context (for dealing with plural things). Many of the traditional operators we've described so far have been strictly scalar in their operation. They always take singular arguments (or pairs of singular arguments for binary operators), and always produce a singular result, even in a list context. So if you write this:

@array = (1 + 2, 3 - 4, 5 * 6, 7 / 8);

you know that the list on the right side contains exactly four values, because the ordinary math operators always produce scalar values, even in the list context provided by the assignment to an array.

However, other Perl operators can produce either a scalar or a list value, depending on their context. They just "know" whether a scalar or a list is expected of them. But how will you know that? It turns out to be pretty easy to figure out, once you get your mind around a few key concepts.

First, list context has to be provided by something in the "surroundings". In the example above, the list assignment provides it. If you look at the various syntax summaries scattered throughout Chapter 2, The Gory Details and Chapter 3, Functions, you'll see various operators that are defined to take a LIST as an argument. Those are the operators that provide a list context. Throughout this book, LIST is used as a specific technical term to mean "a syntactic construct that provides a list context". For example, if you look up sort, you'll find the syntax summary:

sort LIST

That means that sort provides a list context to its arguments.

Second, at compile time, any operator that takes a LIST provides a list context to each syntactic element of that LIST. So every top-level operator or entity in the LIST knows that it's supposed to produce the best list it knows how to produce. This means that if you say:

sort @guys, @gals, other();

then each of @guys, @gals, and other() knows that it's supposed to produce a list value.

Finally, at run-time, each of those LIST elements produces its list in turn, and then (this is important) all the separate lists are joined together, end to end, into a single list. And that squashed-flat, one-dimensional list is what is finally handed off to the function that wanted a LIST in the first place. So if @guys contains (Fred,Barney), @gals contains (Wilma,Betty), and the other() function returns the single-element list (Dino), then the LIST that sort sees is

(Fred,Barney,Wilma,Betty,Dino)

and the LIST that sort returns is

(Barney,Betty,Dino,Fred,Wilma)

Some operators produce lists (like keys), some consume them (like print), and some transform lists into other lists (like sort). Operators in the last category can be considered filters; only, unlike in the shell, the flow of data is from right to left, since list operators operate on their arguments passed in from the right. You can stack up several list operators in a row:

print reverse sort map {lc} keys %hash;

That takes the keys of %hash and returns them to the map function, which lowercases all the keys by applying the lc operator to each of them, and passes them to the sort function, which sorts them, and passes them to the reverse function, which reverses the order of the list elements, and passes them to the print function, which prints them.

As you can see, that's much easier to describe in Perl than in English.


Previous Home Next
Regular Expressions Book Index What You Don't Know Won't Hurt You (Much)