Bits and bobs

This post is a bit of a rag-bag of useful stuff that doesn’t fit elsewhere.

Creating code at runtime

There’s another way of creating code on the fly, besides closures. This is eval. eval comes in two flavours: string and block. The block eval looks like:

$number = <STDIN>;
my $answer;
eval {
    $answer = 2 / $number;
};
print $@ ? "Divide by zero error or similar: $@" : $answer;

This is a useful (and indeed the only way) to catch exceptions, that is, divide-by-zero errors and their ilk. Note you need a ; at the end of the eval { BLOCK }; because eval is a statement. If something goes wrong in the block, the special variable $@ is set with what went wrong. So eval-ling a block allows you to test Perl code, and make fatal things non-fatal.

If you actually want to create new code on the fly, you can use string eval:

my $name = <STDIN>;
eval "sub $name { return \"hello\" } ";

This will create a subroutine on the fly called $name that returns ‘hello’, which you can then call normally. The string is quoted, and the usual rules for double quoted string interpolation apply. eval is very powerful (i.e. dangerous), which you should be aware of before you even think of using it:

my $cmd = <STDIN>;
eval $cmd;

is going to get you in an awful lot of trouble if someone types in

system("rm -rf *")

on Unix, or

system("DELTREE c:\\windows")

if you’re on Windows. Beware.

What’s the time Mr Wolf?

$time = scalar( localtime );

print $time;

localtime returns the local time in an array, type perldoc -f localtime on a command line for details. The commonest way of using it though, is calling it in scalar context, which returns a useful descriptive string.

Bed-time

sleep 1;

makes perl sleep for 1 second (ish: subject to some iffiness).

Time to get up

print "\a";

\a is an annoying alarm beep.

Pretty printing

Perl has two functions for this, printf and sprintf. Both use the same syntax, but printf actually prints the prettified string, whereas sprintf just returns it, so you can store the prettified version elsewhere, e.g. in a variable. The format is a very complex: perldoc -f sprintf for the gory details.

printf takes at least two arguments. The first is a control string, the second is the string to prettify. Control strings contain placeholders that start with %. They end with a letter that indicates the format you want: f is a fixed decimal floating point number. e is a scientific notation float, s is a vanilla string, and u is an unsigned (no + or −) integer, and so on. Between the % and the letter can come some bits and bobs to specify the format you want your string in. If you put a number in, it specifies the minimum field width. If you put a - in between, it means left justify, so:

my $string1 = "carrots";
my $string2 = "beans";
printf(
    "%-10s neatly lined up\n%-10s neatly lined up too",
     $string1,              $string2
);
carrots   neatly lined up
beans     neatly lined up too

See that the %-10s in the control string act like place-holders for the list of strings that follow. You can format your code nicely so that its readers will know which placeholder refers to which string.

Another useful one is:

my $string = "1.23465326362643743657563";
printf( "%.3f", $string );
1.235

A . followed by a number indicates a maximum number of decimal places. Incidentally, if you need to print a literal % character in a control string, you’ll need to escape it, like this: %%. I won’t cover them here, but another way of messing with strings is using the pack and unpack operators, which allow you to convert between strings (like “100”) and (for example) their binary equivalent (i.e. the actual 8-bit binary string 00000100). perldoc -f pack for the details.

Loop control

If you have a bunch of loops nested in each other:

while ( <$FILE> ) {
    while ( my $word = split /\s+/, $_ )     {
        print "$word" unless $word =~ /^do_not_print_me$/i;
    }
}

You’ll often want to abort one or other of them prematurely. For this you’ll want next and last. Both drop you out of the current innermost loop: next skips any remaining code, and restarts the loops with the next value, whilst last kills the innermost loop dead. In this case next will move onto the next $word, whilst last will ignore the rest of the $words generated by split :

while ( <$FILE> ) {
    while ( my $word = split /\s+/, $_ ) {
        next if $word =~ /^#/;
            # ignore any 'word' starting with a #
        last if $word =~ /^END_OF_LINE$/;
            # ignore the rest of the words in the line
        print $word;
    }
}

The problem with this is that maybe you want to drop out of the outer loop if you find something in the inner loop. To do this, you can use labels. For example, if you were trying to parse a Perl file (not a good idea: the only thing that can parse Perl code properly is the perl interpreter), you might try something like this:

LINE: while ( <$FILE> ) {
    WORD: while ( my $word = split /\s+/, $_ ) {
        next LINE if $word =~ /^#/;
            # ignore the rest of the line, it's only a comment
        last LINE if $word =~ /^__END__$/;
            # ignore the rest of the lines if you find Perl's __END__ token
        next WORD if $word =~ /rude/;
            # don't print anything rude
        print $word;
    }
}

The LABEL:s allow you to jump out of a loop from any depth. The much deprecated goto LABEL allows you to jump to anywhere in the code, but now I’ve told you about it, forget about it, there is a whole world of hurt there. There’s also redo which makes for the simplest loop possible:

LOOP: {
    print "Hello. Did you know you can kill a perl program with Control-C\n";
    redo LOOP;
}

This sort of loop seems to be looked down upon, but I’ve found it useful from time to time.

Heredocs

Heredocs are a way of including a large block of text in a Perl script without having to quote it line by line:

my $name = "Stewie Griffin";
print <<"THIS";
Hello,
This is a double quoted heredoc, as you can see from the fact that the
THIS is written with double quotes. This means you can interpolate  variables
like $name. If you use single quotes around the THIS, the heredoc follows
single-quote rules. There is one thing different: you no longer need to escape
"quotes" like in a normal quoted string. This is because the end of the string
is terminated by the THIS label, so quotes don't mean anything special.
The THIS label must be on a line on its own, up against the margin, like
THIS

$string = <<'HERE';
This also works: you can assign a heredoc to a string. The token HERE is
single-quoted so $name will not interpolate.
HERE

print $string;

I generally prefer to use large descriptive tokens like __MESSAGE_BODY__ as they stand out better.

Next up…installing modules.

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.