@@TITLE Statements@@ @@SEQUENCE 5@@
A statement is sort of a full sentence of instructions, made up
from one or more expressions. Statements usually cover no more than
a single line of code. Sometimes it's necessary to break it up
though if it becomes too long simply to improve on legibility -- a
little like avoiding long run-on sentences. For most statements you
simply break the line between two words, but if you are in the
middle of a string you need to add a backslash (\) at
the end of the line in order for the gamedriver to understand
what's going on.
DRIVER->message("This is an example of \
a broken string.\n");
However, breaking a statement with backslash is extremely ugly
and makes the code hard to read. It's usually possible to break the
line naturally at the end of a string, or between two operators of
some kind, or even just split the string in half and add the two
parts together with the + operator. The only time the
backslash really is necessary is in
#define-statements, which we'll mention later.
DRIVER->message("This is a better way of " +
"breaking a string.\n");
Statements in LPC almost always end with ;. It's
considered good practice to put a newline there as well. That is to
say, you shouldn't put multiple statements on the same line if you
can help it. Keeping them separate makes them easier to read.
There are a lot of statements
such as conditional statements that in certain circumstances
execute only one specified statement and no more. Suppose
you want to have several statements executed and not just one?
Well, there's a special statement called block statement
that will allow you to do that. A block is defined as starting with
a { and ending with a }. Within that
block you may have as many statements of any kind (including
variable definitions) as you like. The block statement shouldn't
end with a ;, though usually it doesn't matter if you
accidentally put one there. Example:
if(my_var < 3)
{
statement_one();
statement_two();
statement_three();
}
As stated ; is mostly used to terminate statements.
However it's also a statement in its own right.
The ; on it's own will simply be a null statement
causing nothing to happen. This is useful when you have
test-clauses and loops (described later) that perform their
intended purpose within the test or loop clause and aren't actually
intended to do anything else. Just remember that anywhere you're
allowed to have a statement, you can just put a ; as a
statement that does nothing.
Conditional statements are used a lot in LPC, and there are
several ways of writing them. A very important concept is that
0 and nil are considered as false
and any other value as true in tests. This means that empty
listings ({}), empty strings "" and empty
mappings ([]) also are evaluated as true since
they aren't 0 or nil. You have to use
special functions to compute their size or determine content if you
want test them.
The most common conditional
statement is the if statement. It's easy to use and
can be combined with an else clause to do two
different things based on a variable's value. It's written like
this:
if (expression) statement;
e.g.
if (a == 5)
a -= 4;
If you want to handle the false case, you can add an
else statement like this:
if (expression) true-statement else false-statement;
e.g.
if (a == 5)
a -= 4;
else
a += 18;
or
if(a > 10)
a -= 10;
else {
a += 100;
b--;
a -= 10;
}
If one variable has to be tested for a lot of different values, the resulting list of `if-else-if-else' statements soon gets very long and hard to read. However, if the type of the value you are testing is an integer, a float or a string you can use a much denser and neater way of coding. Assume you have the following code you want to write:
if (name == "fatty")
{
nat = "se";
desc = "blimp";
}
else if (name == "plugh")
{
nat = "no";
desc = "warlock";
}
else if (name == "olorin")
{
nat = "de";
desc = "bloodshot";
}
else
{
nat = "x";
desc = "unknown";
}
A better way of writing this is:
switch (name)
{
case "fatty":
nat = "se";
desc = "blimp";
break;
case "plugh":
nat = "no";
desc = "warlock";
break;
case "olorin":
nat = "de";
desc = "bloodshot";
break;
default:
nat = "x";
desc = "unknown";
}
The workings of this statement is simple: switch
sets up the expression value within the parenthesis for matching.
Then every expression following a case is compared to
find a match.
Note that the case expression must be a
constant value. It can't be a variable, function call or other
expression.
After a match has been found the following statements are
executed until a break statement is found. If no
matching value can be found, the default statements
are executed instead.
While it's not mandatory to have a default section,
it's highly recommended since that usually means that something has
happened that wasn't predicted when writing the program. It's
usually very good to have an error message to notify the user (or
you) that something unexpected happened.
If you forget to put in a 'break' statement the following 'case' expression will be executed. This might sound like something you don't want, but if in the example above the names `fatty' and `plugh' both should generate the same result you could write:
case "fatty":
/* FALLTHROUGH */
case "plugh":
< code >
break;
... and save a bit of space. Writing code with switch doesn't
make it any quicker to execute. It does make it a lot easier
to read, which reduces the chance of making mistakes while coding.
Remember to put the /* FALLTHROUGH */ comment there
though, or it might be hard to remember later if it was intentional
or an omission of a break statement. If you have code
that's executed before falling through to the next case, this is
especially important. A good idea is usually to add an extra
linefeed after a break statement just to give some
extra 'breathing space' to improve on legibility.
This is a very condensed way of
writing an if/else statement and return a value
depending on how the test turned out. The ?: operator
isn't a statement, it's an expression since it returns a value.
It's listed here instead of being listed among the expressions
because it's effectively a conditional, though.
Suppose you want to write the following:
if (test_expression)
var = if_expression;
else
var = else_expression;
You can write that in a much more condensed way:
var = test_expression ? if_expression : else_expression;
e.g.
name = day == 2 ? "tuesday" : "another day";
Opinions vary as to whether writing the conditional with this
operator makes the code easier or harder to read. A common rule of
thumb is that one use of the ?: operator makes code
clearer, but that several in a single statement only makes it
worse. Something like this definitely isn't an
improvement:
name = day == 2 ? time == 18 ? "miller time" : "tuesday" : "another day";
There are two loop statements in LPC which incorporate the use of conditional statements within them. That means they can be programmed to execute only until a certain condition is true.
If you want a counter or an iterator, you should usually use the for statement. The syntax is as follows:
for (initalize_statement ; test_expression ; end_of_loop_statement)
body_statement;
When first entered, the for statement executes
the initialize_statement part. This part usually is used to
initialize counters or values for the loop itself. Then the first
loop starts. Every loop starts by executing the
test_expression and examining the result. This is a truth
conditional, so any answer not equal to 0 or
nil will cause the loop to be run. If the test
expression is true then the body_statement is executed,
immediately followed by the end_of_loop_statement. In the
body_statement you usually do what you want to have done for
this iteration. In the end_of_loop_statement you usually
increment or decrement counters as needed to prepare them for the
test_expression in the next loop.
Throughout the previous section I used the word usually a lot. This is because you don't have to do it that way, there's no rule forcing you to make use of the statements in the way I said. For the moment let's stick to the regular way of using the for-statement. Later on I'll describe more refined techniques, and you can discover your own as well.
Assume you want to compute the sum of all integers from 7 to 123 and don't know the formula ((x2^2 + x1^2) / 2). The most straightforward way of doing that is a loop.
result = 0;
for (count = 7 ; count < 124 ; count++)
result += count;
First of all, result is set to 0. Then the actual
for statement is entered. It begins by setting the
variable count to 7. Then the loop is entered, beginning by testing
if count (= 7) is less than 124. It is, so result has
count added to it. Then count is incremented and the loop is
entered again. This goes on until the count value reaches 124.
Since that isn't less than 124 the loop is ended.
The loop form above is pretty standard in C, but you may have realized there's another way you can write the same thing:
result = 0;
for (count = 7 ; count <= 123 ; count++)
result += count;
This way works fine too, and you may find it more
understandable. Then again, you may not.
Please note that the value of count after the for
statement will be 124 and not 123. The
test_expression must evaluate to false in order for
the loop to end, and in this case the value for count then must be
124. This is true for both ways of writing the loop
above!
The while statement is
pretty straightforward. You can probably guess from its name what
it does. The statement will perform another statement over and over
until a given while expression returns false. The
syntax is:
while (<test expression>)
Note carefully that the test expression is checked first of all, before running the statement the first time. If it evaluates as false the first time, the body is never executed.
a = 0;
while (a != 4)
{
a += 5;
a /= 2;
}
Sometimes during the execution of switch,
for or while statements it becomes
necessary to abort execution of the block code, and continue
execution outside. To do that you use the break
statement. It stops the execution of that block and continues after
it.
while (end_condition < 9999)
{
/* If the time() function returns 29449494, abort execution */
if (time() == 29449494)
break;
< code >
}
/* Continue here both after a break or when the full loop is done. */
< code >
Sometimes you merely want to start over from the top of the
for or while loop you're running. To do
that, you use the continue statement.
/* Add all even numbers */
sum = 0;
for (i = 0 ; i < 10000 ; i++)
{
/* Start from the top of the loop if 'i' is an odd number */
if (i % 2)
continue;
sum += i;
}
Notice that the i++ is executed when the loop is
continued. Only the sum += i; is skipped.