In Perl, there are several ways you can affect control flow. That is, you might want to execute a block of code several times, or you might want to execute something conditionally.
A while loop will keep going until a continuation contidition is met. For instance,
while($currentLine ne '') {
my($currentToken, $currentLine)
= split(/\s+/, $currentLine, 2);
# Do something with the current Token...
}
This will take a line of text and peel off a word at a time, assuming that
the words are separated by whitespace (\s+ is equivalent to any series of
spaces, tabs, and newlines). If you were writing a real parser, you would
also want to take into account the possibility of one of the words itself
containing spaces, and think about what happens with quotes and escaped
quotes. Those situations come into play when parsing Inventor or Maya
files. But if you are making your own configuration files, it is probably
good enough.
split is a command intrinsic to Perl which breaks
up a string into a list. It uses the first argument as a regular expression
to determine what the separator is. In this case, I'm using whitespace.
Other common ones are slash(/\//), underscore (/_/),
and colon (/:/). The second argument is the string that I am
splitting. Note that the split by itself will not alter the
$currentLine. But since I assign to it on the left hand side
of the =, it will hold everything remaining on the line.
Now, normally, split is used to break apart a line into all of the components. However, you can use the optional third arguement to limit the number of tokens. In this case, I use 2, so I only get the next token and "the entire rest of the line."
Another common example is to process the command line. These
days, I tend to use GetOpt::Long to process any serious command
line, but when people first start, they often use something like:
while (scalar(@ARGV)>0) {
my $currArg = shift @ARGV;
# do something with the arg
}
Incidentally, I don't make a lot of custom configuration files anymore. I tend to keep most of my data in Perl files to start. It saves me the trouble of writing a parser later.
The basic structure of a for loop is:
# $fs is frame start
# $fe is frame end
# $fi is frame increment
for (my $frame = $fs; $frame<= $fe; $frame += $fi) {
# do something with each $frame...
}
You can probably figure it out just by looking, but I'll go over it anyway:
$frame to
$fs, fs is just a name given to "frame start" in a lot
of command lines for a lot of programs. It is not a standard
variable. I'm assuming you already set it earlier in the code.
my $frame = $fs;
while ($frame <= $fe) {
# Do something with each frame
$frame += $fi;
}
undef $frame;
Any for loop can be decomposed this way. But it is just a
convenience to be able to express it in one line.
This loop basically takes a frame range and an increment and does something to each frame. You'll probably do that a lot in a pipeline.
foreach loops are a really convenient way to do
something to every element in an array. You could do this in a
for loop like this:
@elements = ('BigRed', 'Whitney', 'Courtney', 'Darcy', 'Carver');
for( my $i=0; $i < scalar(@elements); $i++) {
my $element = $elements[$i];
# Do something with the element...
}
but Perl offers the alternative of the foreach loop which will
access each element of the array.
@elements = ('BigRed', 'Whitney', 'Courtney', 'Darcy', 'Carver');
foreach my $element (@elements) {
# do something with the element
}
This is much more convenient, and doesn't need the $i. A lot
of the time, when you are iterating through the elements, you really don't
care what the index was, so this saves you the hassle. A couple more notes:
my is optional. Actually, I haven't figured out
what the difference between using it and not using it is, because the
variable seems to be scoped to inside the loop anyway, at least with
Perl 5.6, it seems to be the case. I just like declaring the
my because it is basically being a temporary value.
my $element thing is optional.
If you don't declare a variable name yourself, the foreach
will stick the value in $_.
$element inside the loop, it will
change the value of that element in the @elements array.
Some other common uses of the foreach loop are:
for
example, I gave the example of a continuous range of frames, but if
you just have a random set of frames, the foreach loop
can help you here too:
@frames = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31);
foreach my $frame (@frammes) {
# do something for each frame
)
foreach my $pattern (keys %materials) {
# do something with the $pattern and the $material{$pattern}
# like maybe check a patch to see if it matches the pattern
# then bind it to the material for that pattern
}
if is one of the simplest structures. Basically, it
will only execute a code block if the condition is satisfied. For example,
let's say you want to print a help message if someone executes your script
without entering any parameters.
if (scalar(@ARGV)==0) {
$needsHelp = 1;
}
# process some stuff the help message would want to
# know about...
if ($needsHelp) {
print "Some instructions about how to use this program\n";
exit 0;
}
There's an alternate version of this. You can also put the if after a
single statement. I very rarely do this. In fact, I think the only time I
do this is when I use next in a loop.
When you're using while, for, or
foreach, you can jump to the next iteration prematurely by
using next, or you can break out of the loop by using
last. I will grant you that it's a little weird that they
did't use "continue" and "break," but take what you can get.
This is used like:
@elements = ('BigRed', 'Whitney', 'Courtney', 'Darcy', 'Carver');
foreach my $element (@elements) {
next if ($element eq 'BigRed'); # BigRed already graduated
# Add all other elements to the image
&renderToTeamPhoto($element);
}
The above example will send each element to the user subroutine
renderToTeamPhoto. However, for the first one, since it
is 'BigRed', the conditional evaluates to true, so the next
is executed. This is equivalent to saying:
if ($element eq 'BigRed') {
next;
}
but it is just shorter. I only do this for next, and I try to keep the
conditionals short (like only one line).
Perl also offers a couple more sturctures with until and unless that allow you to define a terminating/continuing condition after the code block. In general, I don't like these because I prefer to see the condition up front, so I'm not going to go into these. If you really want to learn about these, read Learning Perl [SCHW97] and Programming Perl [WALL00] .