Thinking In Perl

When to Declare

Most beginner-level programming instruction has programmers declaring all of their variables at the top of the file or function. This methodology completely ignores Perl's built-in garbage collector. The Perl way to do things is to declare variables in the smallest scope possible. This way, you can ensure that variables such as loop counters only use available memory while they need to be using that memory. The following code is prevalent in scripts all over:

While this code is correct and passes the strict pragma, it's very inefficient. If this program were longer, say 6,000 lines, and a programmer needed to track down what's happening to $letter, it'd be difficult to tell exactly what's happening because it's declared at the beginning of the program and not used until much later. This code demonstrates one more Perl-ish way to acheive the same goal:

Not all the constructs may make sense to a new Perl programmer, but the snippet is intended to demonstrate where to declare variables. Use of the my() in the loop declaration scopes the variables to that loop. At the end of the loop, that variable is garbage collected by the Perl interpretter.

Variable Context

One of the most interesting features Perl provides is variable context. This allows a variable to have more than one value, depending on the surrounding code. This is a concept that comes straight from human language and makes a lot tasks much easier. Variables can be evaluated in scalar (singular) or list (plural) contexts. Most Perl functions and operators can operate or return in either context. The help files briefly touch on this, but they fail to demonstrate the full power of variable context. Consider the following:

print localtime(time());

The perldoc page for localtime() says it returns a list in list context, and in scalar context it returns a system formatted date string. To many beginner's surprise, the above code is actually invoked in list context. Calls to functions expect a list of parameters to be passed to a function and because of Perl's unique syntax for parameter passing, functions always invoke list context. To get the example to print the system formatted, human readable time, one must force scalar context. This is relatively easy with the scalar() function.

print scalar(localtime(time());

Arrays and hashes are created from lists. The () operator forces list context. Arrays and hashes also can be flattened into one list by using this list context. It's often used to merge two hashes. Hashes are associative arrays, and in their creation, any duplicate keys will be overriden by the last key in the list. If %data contains unique, important data and %filler is a hash that makes sure all the required fields have a value, it is possible to merge them into a hash %complete that contains only the unique keys from both %data and %filler and the duplicate keys are matched to values in %data:

Also very useful is that the meaning of arrays in scalar context return the number of items they contain. This can be used to process an array if it contains items, or skip that processing. Perl does not have a boolean type, but this is far from a handicap when you consider that conditional operators force scalar context. This makes testing the number of elements in an array fairly simplistic:

It was not necessary to force scalar context using the scalar() function in the print() statement because @list was prefixed by the string concatentation operator that forces scalar context. This can be confusing to beginners because not many other languages make the programmer be aware of the additional context the variables are being accessed in. With proper instruction and practice, this becomes second nature to a Perl programmer and oft saves them many key strokes. However, consider the output of the following snippet:

This is why Perl can become so unbearable for programmers coming from other languages because context may not be a familiar concept. In the first print(), the @array is being forced into scalar context by use of the string concatenation operator (.). In the second print(), the @array is being passed as part of the list to the print() function. The comma is one of two list delimiters (, and =>) and maintains the list context of the function call. Even more confusing is the result of the third and fourth print() statements. Replacing just one of the commas in the above example forces @array to be evaluated in scalar context because the . operator requires a string (scalar) on BOTH sides of the operator.

The number of arguments being passed might shed some light on what's happening. In the first example, thanks to everything being concatenated, the list of paramters to the print state contains only one element, the entire string. The second exaple is actually passing 14 elements to the print function, the two strings at the beginning and end, and the 12 elements in the @array. The third example passses only two elements to the print statement; the string formed by the concatenation and the string " items\n". The fourth example passes two parameters as well: the string "List contains " and the string formed by the concatenation.

As with any other programming language, sometimes it is better to waste a few extra key strokes to assure readability. In the above snippet, it would be best to prefix the @array with a scalar() function call to ensure there's no confusion during future edits.

A quick guideline of the variables and their meaning in each context:

Scalar is a single item list in list and array context.

Lists in scalar context evaluate to their last element.

Arrays in scalar context evalute to the number of elements they contain.

Learning what functions and constructs affect context can take some time, but here are some basic rules.

Conditional Operators evaluate expressions in scalar context.

Anything inside of (..), [..], and {..} is evaluated in list context.

Function argument lists are lists (shocker).

(..) is the list operator, and envokes list context on its contents.

The right side of an assignment expression is evaluated based on the context of the left side of the expression. Some examples include:

The Tip of The Iceberg

These examples and concepts in this article are just barely scratching the surface of the world of Perl programming. The concepts presented were only viewed at 10,000 feet. Luckily, interested Perl programmers have excellent documentation available to them at the tip of their fingers with the 'perldoc' command that ships standard with Perl. Running 'perldoc perl' presents a programmer with a long list of manuals and guides that they may drill down into. Relevant topics in the Perl core documentation include 'perldata', 'perlref', and 'perllol'.

About the Author

Brad Lhotsky is a Software Developer whose focus is primarily web based
application in Perl and PHP. He has over 5 years experience developing
systems for end users and system and network administrators. Brad has
been active on Perl beginner's mailing lists and forums for years,
attempting to give something back to the community.