List and Scalar Context

Before we leave arrays behind, I'd like to pause significantly to discuss the subject of context. Context, in Perl, is the notion that different bits of data act differently depending on what you're trying to do with them. Context can be particularly baffling to understand, and if you're confused about context you can end up with results that seem to make absolutely no sense (or bits of other people's scripts that produce results seemingly out of nowhere). On the other hand, when you understand context in Perl, and how different operations use context, you can do very complex things very quickly, which would take several lines of code in other languages.

Note

If you're an experienced programmer and you've been scanning the book for the important stuff up to this point, stop it right now. Take the time to read this section carefully and make sure you understand it. Context can trip up both novice and experienced Perl programmers alike.


What Is Context?

So just what does context mean? Let's use an analogy from English: take the word contract. Define it, in 25 words or fewer.

What's the definition of the word contract? The correct answer is; actually, “It depends on how you use it.” In the sentence, “I just signed the contract,” it's a noun, and the definition of contract as a noun is “a legal agreement between two parties.” In another sentence, “Extreme cold causes the rivets to contract,” it's used as a verb, and the definition is “to become smaller in size or length.” Same word, same spelling, but a different meaning (and, actually a different pronunciation) depending on whether it's used in a noun context or a verb context.

“Okay,” you say, “but what does this have to do with Perl?” I'm getting to that. Take the simple Perl expression 5 + 4. What does that expression evaluate to?

“Uh, 9,” you reply, wondering if there's a catch. You bet there's a catch. What if you stick that expression into a test, like this:

if (5 + 4) { ... )

Now what does 5 + 4 evaluate to? Well, it still evaluates to 9. But then, because it's being used as a test, it also evaluates to true (remember, only 0 and "" are false). In the context of a test—Perl folk call it a boolean context—the expression 5 + 4 evaluates to a boolean value, not a number. The if construct expects a true or false value for the test, so Perl happily gives it one.

When numbers and strings are automatically converted from one to the other, they're converted based on context. In arithmetic, Perl expects numeric operands (a numeric context), so it converts any strings to numbers. The string operators, in turn, have a string context, so Perl converts numeric values to strings to satisfy the context.

Numeric, string, and boolean contexts are all forms of the more general scalar context. For the most part, you won't have to worry a lot about differentiating between the three because Perl can figure it out and convert it for you.

Where the complications arise is with differentiating between scalars and lists. Every operation in Perl is evaluated in either a scalar or list context. Some operations in Perl expect a scalar context, others expect lists, and you'll get errors in Perl if you try to stick the wrong kind of data where the other is expected. A third—and very common—class of operations can be evaluated in either a scalar or list context and might behave differently in one context than they do in the other. And, to make matters even more complex; there are no standard rules for how lists behave in a scalar context or vice versa. Each operation has its own rules, and you have to keep track of them or look them up. For every operation in Perl, then, if you're mixing lists and scalars, you should be asking yourself three questions:

  • What context am I in (scalar or list)?

  • What type of data am I using in that context?

  • What is supposed to happen when I use that data in that context?

For the rest of this section, then, I'll discuss a few instances of when context is important. But this is definitely an area that will take further diligence on your part as you develop your skills in Perl. Keep those three questions and the Perl documentation close at hand, and you'll be fine.

Finding the Number of Elements in an Array, Revisited

You've already seen one example of how context is important. Remember how to find out the length of an array, using this line?

$numelements = @arrayname;

I told you then to just learn the rule and not worry about it. This is a classic example of where context can be confusing in Perl. The $numelements variable is a scalar variable, so the assignment to that variable is evaluated in a scalar context—a scalar value is expected on the right-hand side of the = (that's the answer to your first question: What context am I in? You're in a scalar context).

In this example, you've put a list on the right side, in a scalar context (the answer to the second question: What kind of data am I using?). So the only remaining question is, “What's supposed to happen to a list in this context?” In this case, what happens is that the number of elements in the array @arrayname ends up getting assigned to the $numelements variable. An array variable, evaluated in a scalar assignment context, results in the number of elements in the list.

Don't think of it as converting the list to a scalar; it doesn't work that way. There's no conversion going on. The list simply behaves differently in this context than it does elsewhere.

You can use this number-of-elements feature by supplying an array variable anywhere a scalar value is expected—for example, in any of these expressions:

$med = $arrayname[@arrayname / 2]; # median value, remember?
$tot = @arrayname + @anotherarray + @athird;
if (@arrayname > 10) { ... }
while (@arrayname) { ... }

If this seems confusing or difficult to read, you can always stick to the simple assignment of a scalar on one side and an array variable on the other. Or, if you're only getting the length of the array so that you can iterate over its values, you can use $#array as the stopping value instead of the number of elements, and avoid the whole thing.

Context and Assignment

Assignment is probably the most common case in which context becomes important—or, at least, it's one of the places where context is easiest to explain. With the left and right side of the assignment operator, you can have contexts that match (scalar = scalar, list = list), or mismatched contexts (scalar = list, list = scalar). In this section, I'll go over some of the simple rules for how context works in assignments.

Let's start with the easy cases, where the context matches. You've already learned all these. Scalar to scalar is an individual thing to an individual thing, with numbers and strings converted at will:

$x = 'foo'; # scalar variable, scalar value

You've also learned about list-to-list assignments, with list syntax and array variables on the left and right sides of the assignment operator, as well as nested inside other lists:

@nums = (10, 20, 30);
($x, $y, $z) = @nums;
($a, @nums2) = @nums;

Now let's look at the cases where the context doesn't match. Say you try to assign a scalar in a list context, as in either of these examples:

@array = 3.145;

($a, $b) = $c;

This one's easy! In these cases, the scalar value on the right is simply converted into a list and then assigned using list assignment rules. @array becomes a list of a single value, 3.145, $a gets the value of $c, and $b becomes undefined.

The hardest case is dealing with assigning a list on the right to a scalar on the left. You've learned what happens when you assign an array variable to a scalar variable (you get the number of elements in the array):

$numelements = @array;

You'll also get the number of elements in the list if the value on the right is a raw list:

$numelements = sort @array;

When you use actual list syntax, however, the rule is different:

$x = (2, 4, 8, 16);

In this case, the rule is that all the values in the list except the last one are ignored. The value of $x here will be 16. Note that this is also a different rule from assigning the list ($x) to that same list, as list-to-list assignment starts from the first element, 2, and discards unused elements.

The most important thing to remember about these contexts is that there is no general rule for how a list behaves in a scalar context—you just have to know the rules. Keep those three questions in mind and you should be fine.

Other Contexts

There are a few other contextual situations worth touching on—things that you should be aware of as you work with lists and scalars.

First, there's boolean context, where a value is tested to see if it's true or false (as in the test for an if or a while). You've already learned how a scalar value, in boolean context, is true if it has any value except "", 0, or undefined.

Lists in boolean context follow a similar rule—a list with any elements, including undefined elements, is true in a boolean context. An empty list is false.

The second situation where context might be important is with functions. Some functions take a list as their argument, and all their arguments are combined and evaluated in that context. If you use functions with parentheses around their arguments, as you learned about yesterday, then there's no problem—you're giving the function a list of arguments. If you don't use parentheses, however, Perl will try and build a list from the arguments you give it. If those arguments contain lists or parenthesized expressions, however, Perl might get confused. Take this example with print, which is one of the functions that expects a list context:

print 4 + 5, 6, 'foo';

In this case, the arguments to print are evaluated as if they were the list (9, 6, 'foo'). With this case, however, the rule is different:

print (4 + 5), 6, 'foo';

Because of that parenthesized expression, Perl will assume that the 4 + 5 expression is its only argument, and become confused about what the 6 and the 'foo' are doing hanging off the end. If you have Perl warnings turned on, it'll catch these (and complain about using constants in a void context). In this case, it's best to solve the problem by parenthesizing the entire list of arguments so there's no ambiguity:

print ((4 + 5), 6, 'foo'),

Most of the time, Perl can figure out whether parentheses mean a function call, an expression, or a list. And, in most of the remaining cases, Perl warnings will help you figure out what's going on where there's ambiguity. But keep the differences and context in mind as you write your Perl scripts.

The scalar Function

Sometimes you really want to use a list in a scalar context, but it's awkward to go out of your way to create a scalar context for that operation (for example, creating a temporary scalar variable just to force the list into a scalar context). Fear not, there is a shorthand way. You can always force a list to be evaluated scalar context, using the scalar function. For example, take the following two statements:

print @nums;
print scalar(@nums);

The print function evaluates its arguments in a list context (this is why you can specify multiple arguments to print separated by commas). The first of these statements, then, expands the @nums array in a list context, and prints the values of that array. The second forces @nums to be evaluated in a scalar context which, in this case, prints the number of elements in @nums.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset