27

Chapter 3:

A Step Into Syntax

Take care of the sense,
and the sounds will take care of themselves.

-Lewis Carroll, Alice's Adventures in Wonderland

In Chapter 1, we developed a program to allow a new cook to prepare hard-
boiled eggs. We started with an English description, later moving to a pseu-
do-code version. In this chapter, we will convert some of the pseudo-code
into actual Perl code. In addition, we will make parts of the program a bit
more general and quite a bit more detailed.

Here, for reference, is the pseudo-code version of our program:

Hard-boiled eggs

# Select some eggs and prepare them for cooking.

Until you have enough eggs:
Quit loop if the carton is empty.
Select an egg from the carton.
If the egg is cracked:
Discard the cracked egg.
Otherwise:
Place the egg in the pan.
Quit program if the pan is empty.
Return remaining eggs to the refrigerator.
Until all of the eggs are covered in water:
Add cold water to the pan.


IMAGE imgs/120.SS01.gif

# Cook the eggs.

Until the water boils:
Heat the pan on a stove burner set to high.
Cover the pan.
Turn off the heat.
Wait 25 minutes.

# Cool and store the cooked eggs.

Until the rinse water stays cool:
Drain and refill the pan with cold rinse water.
Wait 5 minutes.
If any of the cooked eggs are cracked:
Use the cracked eggs first.
If any eggs are not immediately consumed:
Refrigerate the remaining eggs.

The most obvious problem with this program, from the perspective of the
Perl interpreter, is that the statements don't look much like Perl. The inter-
preter, unlike a person, needs to have its instructions written down in a very
exacting form. People can make assumptions and draw conclusions based on
available information. However, an incorrect, misplaced, or missing char-
acter can cause the Perl interpreter to halt in confusion or, worse, perform an
unintended action.

Syntax, Meaning, And Truth

In human languages such as English, the syntaxdefines the way that tokens
(words and punctuation)
1can be assembled. Although it can be very diffi-
cult to determine whether a statement is meaningful (let alone "true"), test-
ing a sentence for correct syntax is usually pretty trivial.

Here are four English sentences. The first one is syntactically flawed, the
second is meaningless, and the third is false. Only the fourth sentence is
syntactically correct, meaningful, and true:

This sentence no verb.
This sentence is not green.

IMAGE imgs/120.SS02.gif

1more generally, lexically joined character sequences (e.g., "Fred" or "PowerPC")


IMAGE imgs/120.SS03.gif

This sentence has two verbs.
This sentence is syntactically correct, meaningful, and true.

Programming languages also have syntax. In fact, the syntax of program-
ming languages tends to be defined much more rigidly than that of human
languages (don't ya know!).

Because Perl syntax is fundamentally algebraic, many Perl statements look
similar to algebraic statements. Perl is not algebra, however; don't assume
that syntactic similarity implies similar (let alone identical) meaning. For
instance, the Perl statement:

$a = $a + 1;# Increment $a

tells the interpreter to retrieve the value of the variable(named storage
location)
$a, add one to it, then place the resulting value back into $a. That
is, this is an executable statement, not an algebraic identity. Other Perl
statements may not look much like algebra, but they dofollow fixed and
well-documented syntactic rules.

When the interpreter first encounters some (putative) Perl code(statements
in Perl syntax), it must parsethe code according to the Perl syntax rules. If
the code is not syntactically correct, the interpreter will issue a diagnostic
message and give up.

The interpreter will not, however, attempt to determine whether the code
is meaningful. Here is a meaningless bit of Perl code, which the interpreter
will parse and execute without any complaint whatsoever:

$a;# Evaluate, then discard, $a

You may be wondering, by the way, about the dollar sign ($) that begins the
variable references above. Perl uses a raft of special characters (essentially
every character on the keyboard!)
2to help it keep track of all the different
kinds of entities it manages. This can be rather confusing at first, but it turns
out to add greatly to Perl's capabilities in the long run.

Hint:The dollar sign looks a lot like an "S". Think of it as a short-
hand way of saying "scalar".

IMAGE imgs/120.SS02.gif

2Foreign readers may encounter some difficulty in attempting to find all of the key-
board characters that Perl uses. We suggest that you use the Mac OS "Key Caps"
program and play with the keyboard settings until you see what you want on the
screen. You may then wish to paste helpful stickers on some of your keys!


IMAGE imgs/120.SS05.gif

The question of program correctnessis even more arcane. One way to define
program correctness might be: Does the program perform the desired set of
operations, under all expected circumstances? This is not an easy question to
answer, given that any significant program can have many different execu-
tion paths. Automated proving of program correctness is, in fact, a hot topic
in Computer Science research, and is likely to remain so for some time.

Nonetheless, it's the job of the programmer to ensure that the code is mean-
ingful and correct. This is best accomplished by adhering to careful design
and coding practices. Rigorous testing can then be used to confirm that the
program performs as expected in a typical range of conditions.

On the other hand, no amount of care and/or testing can guarantee a perfect
program. Consequently, programmers are forced to accept the possibility of
a few bugs in their released code. At present, the best practice is to balance
the implementation and testing costs against the cost of program failure.

So much for cautionary philosophy; now let's examine a few pieces of Perl
code. Please remember that There's More Than One Way To Do It. Hence,
these versions aren't "official syntax", just plausible ways to express the
desired actions.

Expressions, Statements, And Blocks

An expression, in Perl, is a syntactically valid and meaningful sequence of
characters. In general, an expression has a value. Here are some sample
expressions:

123
123 + $i
$j * (123 + $i)

The following lines, in contrast, are not valid expressions:

123 $i
123 ++ $i
$j * 123 + $i)

In use, expressions are collected into statements, as:

$k = 123;
$l = 123 + $i;
$m = $j * (123 + $i);


IMAGE imgs/120.SS06.gif

But, because statements are also expressions, you may encounter some rather
peculiar compound statementson occasion:

$m = $k = 123;
$n = ($j = 123 + $i) * 2;

Syntactically, the difference between a statement and an expression is that
a statement ends in a semi-colon,
;. Semantically, the difference is that a
statement usually does something, while an expression only does things as
side-effects(e.g., by calling a function).

A block, in Perl, is a sequence of statements, enclosed in a matched pair of
braces (curly brackets), as:

{
$k = 123;
$l = 123 + $i;
$m = $j * (123 + $i);
}

Blocks are used to group lines of code for repetition or selection, function def-
initions, or controlling the scope(visibility) of variables. Indenting, as in
the code above, is a good way to clarify the block structureof a program.

Data Structures

Perl provides two powerful data structures (ways of aggregating data):
arraysand hashes. Either of these can contain any number of other items,
including numbers, strings, (references to) other data structures, and more. In
short, by building up combinations of hashes and arrays, you can create just
about any type of data structure you might need.

Although arrays and hashes can act similarly, they are implemented very
differently. An array is an orderedsequence of items, accessed via indexing
or specialized operators. A hash is an unorderedsequence of key / value
pairs
, accessed via (a "hashed lookup" on) the keys (index text strings).

The arrayis Perl's most versatile data structure. Each scalar in an array is
accessed by its position. You can add an element to either end of an array,
remove an element from either end, or index numerically to a specific array


IMAGE imgs/120.SS07.gif

position. You can even modify (groups of) elements in the middle of an ar-
ray,
3possibly changing the positions of any following elements.

In general, arrays are:

  • indexed numerically, like suites in an office complex.

$suite[150] = "Prime Time Freeware";

This code stores a text string ("Prime Time Freeware") in location 150 of
the
suitearray.4

  • used as stacks, also known as "Last-In, First-Out" (LIFO) lists.

$egg = pop(@carton);
push(@pan, $egg);
...
$egg = pop(@pan);

# Get an egg from the carton
# Add it to the pan

# Get an egg from the pan.

This code popsa scalar off of the cartonstack, saves it in $egg, then
pushesit onto the
panstack. Later, it pops a scalar off of the panstack.
Although it doesn't matter in this instance, the scalar that is popped
from the
panstack is the one that was most recently pushed onto it.

  • used as queues, also known as "First-In, First-Out" (FIFO) lists.

push(@belt, $item);
...
$item = shift(@belt);

# Put an item on the belt.

# Get an item from the belt.

This code pushes an item onto the start of a queue that is modelling a
(conveyor)
belt. Later, it shiftsan item off the end of the belt. Note
that the ordering of the items is maintained.

Note:The use of an at-sign (@) before a variable name (e.g., @belt)
tells Perl that we are referring to an entire array, rather than a single
element. You might think of
@as looking a bit like the "a" in "array".
(Don't worry; we'll cover this syntax in detail in following chapters.)

IMAGE imgs/120.SS02.gif

3using the splicefunction
4Pedants might argue that this is actually the 151st location, because (like C) Perl uses
0 as the first array subscript. This will not be an issue in most cases, but you may want
to stuff the information into the back of your mind, in case it comes up at some point...


IMAGE imgs/120.SS09.gif

Hashesare also known as associative arrays. Each scalar value in a hash
is "associated" with a string key(rather than a numeric array index). A
hash can store or retrieve items according to these keys.

Here is an example of a hash being used to store telephone numbers. Note
the use of braces (curly brackets) to surround the index (key), instead of the
square brackets that are used for arrays:

$phone{"Vicki"} = "555-1234";
$phone{"Chris"} = "555-4321";

We'll cover hashes and arrays in more detail later in this book.

Control Flow

Control flow modifiers affect the order in which the program is executed.
They can cause the interpreter to skip statements, repeat actions, etc. Our
program contains several instances of control flow, including the repetition:

Until the rinse water stays cool:
Drain and refill the pan with cold rinse water.
Wait 5 minutes.

In Perl syntax, this might be expressed as:

until(cool($rinse_water)) {
drain(@pan);
refill(@pan);
sleep(300);
}

Perl's untilconstruct performs the testgiven in the parentheses "(...)".
If the test fails, the interpreter executes the statements in the associated
block, then repeats the test. If the test succeeds, the interpreter moves on to
the code following the block.

There are many forms of tests: numeric and string comparisons, file tests, etc.
And, because a test can contain any Perl expression, tests can have an unlim-
ited amount of flexibility. Tests are also used in selection:

If the egg is cracked:
Discard the cracked egg.
Otherwise:
Place the egg in the pan.


IMAGE imgs/120.SS10.gif

This might be expressed in Perl as:

if (cracked($egg)) {
push(@trash, $egg);
} else {
push(@pan,$egg);
}

Perl's if-else construct uses two blocks. The first block is executed if the test
succeeds; the second is executed if the test fails. In many languages, blocks
that contain only one statement do not require braces. This saves on typing,
but it can lead to confusion when control structures are nested(layered). Perl
syntax requires braces for all blocks, eliminating this risk.

Functions And Methods

Functionsare used to remove complicated or frequently-used calculations
from the main flow of the program. This is similar to the way in which
most cookbooks are organized. A generic recipe for "boiling eggs" might be
given, then referenced by recipes that require boiled eggs as ingredients.

Some of the code snippets above have quietly used functions, as:

until(cool($rinse_water)) {
drain(@pan);
refill(@pan);
sleep(300);
}

This snippet depends on the coolfunction to test $rinse_water, returning
a true(non-zero) value when the condition is met. The
drainand refill
functions may or may not return values. This code, in any case, ignores their
return values.

A Perl function may perform some desired action, returna value, or both.5If
a return value makes sense, by all means return one. Don't return a value for
no good reason, though; it just confuses your readers ...

As noted in the previous chapter, Perl has a sleepfunction, which "puts
the program to sleep" for a specified number of seconds. Our program has
two very similar lines, both of which could use this function:

IMAGE imgs/120.SS02.gif

5A function which does not return a value is sometimes called a subroutine; Perl
makes no such distinction.


IMAGE imgs/120.SS12.gif

Wait 25 minutes.
Wait 5 minutes.

# Allow the eggs to cook.
# Allow the eggs to cool.

A brute-force translation of the top line would yield:

sleep(1500);# Allow the eggs to cook.

This is pretty ugly, however. For one thing, the requirements of the sleep
function (i.e., times must be expressed in seconds) have caused us to lose a bit
of readability. Also, hard-coding numbers into programs tends to be a Bad
Idea, from a maintenance perspective. So, let's clean things up:

sleep($cook_time * 60);# Allow the eggs to cook.

Now, if we need to adjust the cooking time, we can do so by changing the
value of
$cook_time. In addition, by using a shorter value, we can produce
a wide range of soft-boiled eggs (three minutes is a good starting point).

The number of seconds in a minute isn't likely to change, and the number 60
is pretty well understood by most readers, so getting rid of it doesn't pay off
much. Still, we can consider using a function to get rid of it, as an exercise:

sleep(min2sec($cook_time));# Allow the eggs to cook.
sleep(min2sec($cool_time));# Allow the eggs to cool.

The function min2secaccepts a single argument, which it multiplies by 60
and returns for use by the calling program. (Don't worry about the syntax of
Perl functions right now, we'll cover it in detail in following chapters.)

Perl also supports methods, as part of its object-orientedprogramming capa-
bilities. Loosely speaking, an objectis a set of data and associated ways of
interacting with the data. For instance, although we have been treating
$panas a simple array, it is quite possible that we might need to maintain
more information on the pan (e.g., is it full?) than that would allow.

But, rather than make the cooking programs deal with the implementation
details of
@panand @trash, we could package up our data structures and
access methods into objects, as:

if ($egg->cracked()) {
$trash->put($egg);
} else {
$pan->put($egg);
}


IMAGE imgs/120.SS13.gif

Although this looks very similar to the non-object version, it is actually
quite different. We are now free to change the implementation details of
the base objects (
$egg, $pan, $trash, ...) without worrying about the
possibility that we might need to modify the programs that use them.

As a beginning programmer, you are very unlikely to get into object design
for a while. On the other hand, you are quite likely to use pre-packaged
objects and methods. Although the syntax can get a bit peculiar at times,
the benefits will be large. (Objects are your friends!)

Parenthetical Remarks

Alert readers may have noted our use of parentheses with several of Perl's
"named operators" (
pop, push, shift, ...). For instance, instead of:

push @pan, $egg;

We have written:

push(@pan, $egg);

The parentheses clarify the fact that @panand $eggare parameters that
"belong to"
push. Although this relationship is relatively self-evident in
the simple statements above, it can become significantly less obvious in
more complex statements.

This is particularly important in a language such as Perl where there are so
many (two dozen!) levels of operator precedence, coupled with left-, right-,
and non-associative operators.

An example may help to clarify the reasons behind our concern. Multiplica-
tion takes precedence over addition and both take precedence over named
operators (read, functions). Hence, these four statements are equivalent:

$hypot = sqrt((($s1 * $s1) + ($s2 * $s2)));

$hypot = sqrt( ($s1 * $s1) + ($s2 * $s2) );

$hypot = sqrt($s1 * $s1+$s2 * $s2);

$hypot = sqrt$s1 * $s1+$s2 * $s2;

We are not convinced, however, that they are equally easy to read. (For
what it's worth, the first version seems quite pedantic to us, while the last
seems very chaotic.) Adding parentheses to clarify meaning is very much a
matter of taste, however, as long as the code actually works.


IMAGE imgs/120.SS14.gif

But, as there is seldom any reason to leave parentheses off of function calls,
we tend to leave them on. We also add parentheses whenever things start
to get too complicated for easy and error-free reading.

This is, of course, acceptable practice in Perl (There's More Than One Way
To Do It, after all!), so we will continue to code our examples in this manner.
Be aware, however, that Perl code generated by Other People may not use
as many parentheses as our code does. (TMTOWTDI works both ways...)

Combinatorial Complexity

For similar reasons, we suggest that you restrict yourself to a subset of Perl's
operators and syntactic structures, at least until you get your feet wet. Add-
ing a new operator or construct to a program (or a language) contributes in a
very non-linear manner to the complexity, because the reader must under-
stand all possible interactions between the new item and the existing ones.

And, although we understand (and to some degree, sympathize with) Larry
Wall's motivation in providing myriad Ways To Do It, we think that some
of the increases in Perl's complexity have not been matched by equivalent
increases in expressive power. And, since TMTOWTDI, we (and you) can
pick and choose among Perl's linguistic features, selecting those that seem
worth the trouble of understanding and remembering.

Copyright © 1997-1998 by Prime Time Freeware. All Rights Reserved.