![]() |
|
Clear questions and runnable code get the best and fastest answer |
|
PerlMonks |
RFC: Context tutorialby kyle (Abbot) |
on Jan 12, 2009 at 17:33 UTC ( [id://735750]=perlmeditation: print w/replies, xml ) | Need Help?? |
This is a draft tutorial on the sticky subject of context. It's terrible and in dire need of your comments. Please, if you value the quality of the tutorials at this fine site, read this with a patch over the uncritical eye and tell me where it's unclear, awkward, and just plain wrong so I can fix it before putting it in the Tutorials section. Don't let it go to those hallowed halls in this sad pathetic state. I wrote it by reading the Camel book's three pages about context, reducing them to nine bullet points, and then expanding the bullet points into this, once I'd forgotten what I read. Really, it needs your help. Thank you. "You will be miserable until you learn the difference between scalar and list context" — Programming Perl 3rd ed, page 69 An introduction to context Context is everywhere in Perl. Every operand is evaluated in some context. Every operator imposes a context to its operands. Every expression is constrained by the context in which it is evaluated. The two main contexts are scalar context and list context. Another is void. An operator that works on scalars imposes a scalar context. You can tell which kind of context an operator has by checking the standard Perl documentation. Every operator that creates a list context has "LIST" in its syntactic description. Context matters because operands in Perl can behave differently according to context. This is sort of like overloading. A function (or other expression) can provide different results depending on the context it's in. Context in action For these examples, I'll use localtime since it's a common function which evaluates differently according to context. In a list context, it returns a series of numeric values that describe the current time. In a scalar context, it returns a human readable string to describe the current time.
In example 1, we assign localtime to an array, which imposes a list context. The array takes in all the values returned by localtime. In example 2, localtime is assigned to a scalar, which imposes a scalar context. In that context, localtime returns the string, "Fri Jan 9 20:51:40 2009".
In example 3, localtime is assigned to a list expression which has three scalar variables in it (wrapped in parentheses). Because of the list expression, localtime() is in list context. The three variables get the first three values from the list that localtime() returns, and its other return values are discarded. Note that this assignment is also to a list expression:
To write it without the list context, drop the parentheses.
Context propagates into sub calls When control reaches a return, the operand to the right of that return finds itself in the context that the sub was in when it was called. That means that in the following code, localtime is in the list context from print.
A sub's calling context might come from some operator high up in the call stack. Forcing scalar context If you want to force a scalar context on an operand, use scalar. For example:
When you print, its operands are in list context. It accepts a list of values to be printed. If you just "print localtime()", it will print something like "40512090109580" (the list of values all stuck together), but "scalar localtime()" looks like "Fri Jan 9 20:51:40 2009". Forcing list context To force a list context where there wouldn't be one otherwise, assign to an empty list.
The "/x/g" in scalar context would match only once, but with the list context, it finds every match. The value of "()=" in scalar context is the number of items in the list. Void context Void context is a special case of scalar context. It's the context of something that doesn't have an operator working on it. The value of a thing in void context is discarded, not used for anything. For example, the body of a while loop is in void context.
The only way void context is like scalar context is that it's not list context. The only way void context is not like scalar context is that some things in void context can generate warnings that they wouldn't generate in a scalar context. Determining context with wantarray A sub can determine what context it was called in by using wantarray. If the sub is in list context, wantarray will return a true value. In scalar context, it returns a false value that's defined. In void context, it will return undef.
More flavors of scalars The Camel book subdivides scalar context into numeric, string, and don't care context, but it's more useful to think of these as casts. Scalar assignment is a "don't care" because numbers and strings are treated the same—passed through to the scalar variable to have and to hold just as they are. Another "don't care" cast is boolean because strings and numbers aren't converted when being interpreted as true or false. To learn more about how values are treated in boolean context, see True or False? A Quick Reference Guide In numeric and string contexts, a variable might undergo a transformation from one to the other in order to facilitate the workings of the operator. In the following example, a string is cast to a number to accommodate the numeric operation of the postfix "--" operator.
To force an expression to a string, use "''." (that is, append to an empty string). To interpret an expression as a number, use "0+" (that is, add zero). It's better to use "0+" than some other identity function (such as "1*") because that's what overload uses to designate the numeric cast. To force a boolean interpretation, use "!!" (that is, boolean negation twice).
Every cast is possible. Strings can be cast to numbers or booleans. Numbers can be cast to strings or booleans. Booleans can be cast to numbers or strings. Context clash A single value (such as a scalar) in a list context becomes a list with one item.
That's simple enough. What about the other way around? In scalar context, a list of expressions separated by commas evaluates to the whatever the last item evaluates to in scalar context.
Note that an array or hash slice counts as a "list" for this behavior. For a description of slices, see Slices. Other things ordinarily used in list context have their own special scalar context behaviors. For a list of these, see On Scalar Context. Interpolative context This isn't a full fledged context like scalar and list context, but it helps to know in which places variables are expanded (interpolated) into their values. For a full list of these, see Quote and Quote-like Operators. For a brief example, see below.
To reiterate, this isn't a context the way "scalar context" is a context, and it isn't what we normally mean when we say simply "context." Still, it may help to think of it as a context when one is considering interpolation. For the full scoop including many other places this is relevant, see the full documentation (Quote and Quote-like Operators). More context If the deep dark magic of wantarray leaves you wanting for more, look into Want and Contextual::Return, which are advertised to detect contexts I have not mentioned here. Also, look at overload for creating an object which can interpret itself differently in numeric and string contexts. Another good source of information about context is What is Scalar Context? by Dominus. Thanks to belg4mit, tye, theorbtwo, Arunbear, bart, zentara, ikegami, Limbic~Region, Narveson, jdporter, and moritz for commenting on an earlier draft! Thank you for reading this far! Having endured so much, please take a short time longer and tell me which parts of it are the most horrendous. Thanks again. Update: I've made small changes based on numerous private comments I've received. (Y'all are welcome to point out my mistakes publicly. I won't be offended.)
Back to
Meditations
|
|