|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
111
|
|
Chapter 8:
|
|
|
|
Curious Constructions
|
|
"Curiouser and curiouser!" cried Alice
(she was so much surprised, that for the moment
she quite forgot how to speak good English)...
|
|
-Lewis Carroll, Alice's Adventures in Wonderland
|
|
In Part I of this book, we introduced a number of programming concepts to
provide you with a firm grounding and a basis for learning about Perl. The
first three chapters of Part II provided an introduction to the Perl language:
its basic structure and syntax, data types, expressions and statements, con-
trol flow, functions, input and output. By now, you know enough to construct
small Perl programs that work.
|
|
The remainder of Part II explores more exotic territory, expanding your hor-
izons (and your programming toolbox). By the time you finish Part II, you
should be able to write (and understand) more complex scripts. You will
then be ready to venture into Part III, where we will cover more advanced
topics and specific applications of MacPerl.
|
|
Subroutines
|
|
In the examples we've seen so far, we've made extensive use of Perl func-
tions. A subroutine is a user-defined function.1Specifically, a subroutine is a
|
|
|
|
1Some languages make a technical distinction between functions and subroutines, but
Perl rarely wastes time on technicalities. However, you will more often see the term
subroutine used for a user-defined function, rather than to a built-in function.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
named piece of code which can be called (by name) from another piece of
code.2The name, subroutine, is self-descriptive.
|
|
A routineis a set of instructions or operations; the prefix "sub-" implies
assistance, or a "part of" something. Thus, a subroutine refers to a set of code
which assists the main part of the program.
|
|
A subroutine is a self-contained portion of a program. Each subroutine per-
forms a specific task. Like the programs they are part of, subroutines can
take input (arguments), process information, and produce output (return
values). A subroutine can be thought of as a "mini program" (a sub-routine!).
|
|
By breaking specific functionality into smaller portions, programming lan-
guages allow for modularityand reuse. In other words, instead of always
writing large, special-purpose programs, we can make use of smaller, gener-
alized units of code which can be moved around and used in many programs.
|
|
It wouldn't be much fun writing programs if you could only use functions that
someone else had created. So, many languages, including Perl, provide for
user-defined subroutines.
|
|
You may recall this sample code from chapter 3:
|
|
sleep(min2sec($cook_time)); # Allow the eggs to cook.
|
|
At the time, we said that the function min2secaccepts a single argument,
which it multiplies by 60 and returns for use by the calling program. Now,
let's write the function min2sec.
|
|
sub min2sec {
$minutes = shift(@_);
return($minutes * 60);
}
|
|
A subroutine definitionbegins with the keyword sub, followed by the sub-
routine name and a block of code to be executed, enclosed in braces, {}. The
return valueof a subroutine is (implicitly) the value of the last expression
evaluated within the block. If you want to be explicit (we recommend it),
use the returnfunction.
|
|
|
|
2OK, we lied. Perl also has anonymous subroutines, which are accessed via references
and are a bit too arcane for the current discussion.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The argument listis passed to the subroutine in the special Perl array, @_.3
The shiftcommand takes the first value off of an array and returns it,
shortening the array by one element in the process. If we did not specify the
array to use, Perl would still use the special @_array. So we could have
said, simply
|
|
$minutes = shift();
|
|
The value that is shifted off is assigned to the variable, $minutes.
|
|
As long as we only want one variable, shiftis a good way to get it. Be a bit
careful, though; if you simply assign to the variable like this:
|
|
$minutes = @_;
|
# error: scalar context!
|
|
you would be evaluating the assignment in scalar context, setting $minutes
to the numberof arguments in the @_array! If, however, there are several
variables being passed to the subroutine, you can use a list to retrieve them.
|
|
($a, $b, $c) = @_;
|
# list context
|
|
Scope
|
|
Any variables created (or used) within a Perl program, which are not
explicitly made private, are global; that is, they (and their current values)
are available to your entire program, including subroutines.
|
|
In practice, variables that are used inside a subroutine are conventionally
made privateto that subroutine, using the my()command.
|
|
my($minutes) = shift();
|
|
These private variables exist only within the scopeof the subroutine; they
are not available to the calling routine or to any other subroutines, not even
to those called bythe current subroutine. When the subroutine returns and
exits, these variables no longer exist (they "go out of scope").
|
|
This type of scoping is called lexical scopingor static scoping. Lexically
scoped variables are declared in a "private dictionary" for each subroutine.
They are visible only from the point at which they are declared until the
|
|
|
|
3Perl has two special "magical" variables, the array, @_, and the scalar, $_. The
former is localto a subroutine; the latter is globalto your program.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
end of the blockin which they were declared (or the end of the file, if the
variable was declared outside a block).4
|
|
The following example illustrates how lexical scoping can be used.
|
|
my($a) = 1;
$b = 2;
print("main:\$a: $a, \$b: $b\n");
print_ab();
|
|
sub print_ab {# a called function
my($a) = 3;
print("sub_ab:\$a: $a, \$b: $b\n");
print_ab2();
}
|
|
sub print_ab2 {# a second called function
print("sub_ab2: \$a: $a, \$b: $b\n");
}
|
|
This displays
|
|
main:$a: 1, $b: 2
sub_ab:$a: 3, $b: 2
sub_ab2: $a: 1, $b: 2
|
|
This script defines two scalar variables - $ais lexically scoped, $bis glob-
al. The value of $ais changed to 3within print_ab, but that value exists
only within the scope of the block. The call to print_ab2()is outside of
the block, so the value of $ais restored to 1.
|
|
Note that the value of $ais available to the subroutine, print_ab2,
because the subroutine is within the same block (in this case, the file) as
the declaration of $a.
|
|
What would happen if you enclosed the first four lines of code within
braces, causing them to be in a separate block from the subroutine
definitions? How would the output be affected?
|
|
|
|
4It may be useful to think of blocks as levels on a "stack"; when the program exits a set
of braces, the definitions made at that level are "popped off the stack".
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{
my($a) = 1;
$b = 2;
...
}
sub print_ab {
...
}
...
|
|
Perl also supports dynamic scoping, by means of the local()command. A
dynamically scoped variable is declared to have a temporary value which
is separate from its external value (outside the block in which it was
localized). Again, the scope lasts from the point of declaration until the
end of the enclosing block (or file). However, "local" variables can be seen
by other subroutines that are called by this subroutine because the variable
is still global; only its valuehas changed.
|
|
Note:It's important to keep in mind that local()isn't really local;
it's still global. Dynamic scoping should be used only when you need to
save, modify, and then restore the value of a global variable. Most of
the time, what you really want to use is my().
|
|
In particular, you can get into trouble using local()declarations if you
set the use strictpragma on your programs. The strictpragma
requires you to fully qualify any global variables (including variable
that have been declared local()) with their explicit package name.
|
|
A Little More About Context
|
|
Recall from the chapter 6 that every operation in Perl is evaluated in a
specific context. We've just seen how context is important in evaluating the
arguments to your subrutine. But what about the subroutine itself - will it
also be evaluated in a particular context?
|
|
The answer is yes. The value your subroutine returns will be evaluated in
the context of the subroutine invocation (the same as any other function).
This means that your own functions can be context-sensitive, just like Perl's
built-in functions and operators.
|
|
If you want to change a subroutine's behavior (e.g., actions, return value)
based on the context in which it was called, use wantarray(). The
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
wantarray()function returns true if the subroutine was called in list
context, false if the call was made in scalar context.
|
|
sub hms {
# return the time (hours, minutes, seconds)
# separately if in list context, or as
# colon separated time if in scalar context
|
|
my($sec, $min, $hr) = localtime();
if (wantarray()) {
return($hr, $min, $sec);
} else {
return(sprintf("%02d:%02d:%02d",
$hr, $min, $sec));
}
}
|
|
The hmssubroutine uses the built-in localtimefunction to determine the
current time in hours, minutes, and seconds. If called in a list context, it
returns the hours, minutes, and seconds as a list. If called in scalar context,
however, the function joins these values into a "timestamp" of the form
12:36:07, suitable for printing.
|
|
Note: localtimeis a very useful function which returns much more
information than just the current hour, minute, and second. In fact, it
returns a nine-element list of useful time and date values, corrected for
the local timezone! We refer you to Reserved Wordsin Part IV, or to
the section on Built-in functions (perlfunc.pod) in the online help,
for more details.
|
|
We're using the sprintffunction to join the values together; sprintf
works very much like printf, except that sprintfcreates a formatted
string, rather than printing its output to a file or to the screen. We could
have written instead:
|
|
return("$hr:$min:$sec");
|
|
but we want to be sure to print each part of the timestamp with a leading 0
for values less than 10. (Hint: what string will the simpler returnstate-
ment, without sprintf, return at 7 minutes after 9?)
|
|
Now, let's try calling our function. We'll call it both ways and display the
results. Decide for yourself whether the context sensitivity adds clarity.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$the_time = hms();# scalar context
print("The time is $the_time.\n");
|
|
@the_time = hms();# list context
print("The time is $the_time[1] minutes ");5
print("past $the_time[0].\n");
|
|
This displays:
|
|
The time is 12:38:35.
The time is 38 minutes past 12.
|
|
When Should You Define a Function?
|
|
You might be wondering when you should create a subroutine (function),
rather than coding everything in the main routine. After all, if your Perl
program isn't really huge; why should you write a subroutine?Why not just
keep all your code together in one big program?
|
|
One reason to create a subroutine is if you need to do something more than
once. After all, how many times do you want to write the same code? In our
egg cooking example in Part I, there were multiple places in the program
where we needed to convert minutes to seconds, in order to wait (sleep) for
the correct length of time. Instead of specifying the multiplication every
time, we used a function.
|
|
Another reason is to reduce the complexity of the main routine. If a piece of
code is complex and has few ties to the surrounding code, break it out as a
separate routine. You'll turn one complex routine into two simpler ones, and
you can ignore the inner workings of the subroutine most of the time.
|
|
Once you have written a function, you can use it in other programs, simply
by including it with their code. Eventually, you can build up a libraryof
useful functions. Instead of deciding how to write something each time,
you'll be able to reuse code you have written (and tested!) before. You may
even decide that your library is useful enough to share!
|
|
The Standard Perl Library
|
|
Perl has nearly 200 built-infunctions (and named operators that act like
functions). Hundreds more are available as part of the standard Perl libr-
|
|
|
|
5We broke this printstatement in two, in order to fit the example on the page.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ary. In programming terminology, a library represents a set of functions that
can be included in your programs. The files that are part of the standard
Perl library can be found in the libsubfolder of the MacPerl folder.
|
|
Many of the functions in the standard library were written by other Perl
programmers, who decided to make their function libraries available to
the Perl community. These library functions are not built into Perl; that is,
they are not immediately accessible to your program at any time. However,
they are included with all Perl distributions and can be readily accessed
with a minimal amount of effort. The Librariespreference tells MacPerl in
which folders it should look for libraries to include.
|
|
If you add your own libraries, we recommend that you store them in a folder
of your own creation. This folder need not be within the standard MacPerl
distribution hierarchy. Just be sure to update the Librariespreference to add
the path to the new folder, so that MacPerl can find your libraries.
|
|
Library functions are usually part of modules.6Module filenames always
end in .pm. Modules that affect the compilation phase of a program as well
as its execution are known as pragmas(e.g., integer). A pragma usually
limits the scope of its effects to a portion of your program (e.g., the inner-
most enclosing block). By convention, pragma names are all lowercase.
|
|
A module is defined as the unit of reusabilityin Perl. Strictly speaking, a
module is a file that defines a package. A package is a way of managing a
chunk of Perl code which is kept separate from other chunks of Perl code.
We will come back to packages and modules in more specific detail in
Chapter 11, Extensibility And Re-use.
|
|
For now, you'll need to understand that a package contains code which is
externalto your program. In order to access this code (e.g., variables, sub-
routines, etc.) from your program, you must (usually) first tell Perl that you
want to include it, by means of the usecommand.
|
|
use Package;
|
|
When you refer to a subroutine (or variable) that is part of a package, you
must also specify the package name, using the "double-colon" syntax, as:
|
|
|
|
6Modules were introduced in version 5 of Perl. You may still encounter libraries
which are not part of modules, usually contained in files which end with the suffix,
.pl. These still work in version 5, but modules onlywork in Perl version 5 and up.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Package::subroutine()
|
|
If the package name is omitted, MacPerl will assume that the current pack-
age (your program, package mainby default) is meant.7
|
|
The standard library contains many of the most popular and useful Perl
modules. For example, the specific functions which allow MacPerl to take
advantage of the Macintosh platform are part of the MacPerl package.
Other packages and modules contain functions that are useful for file man-
ipulation, data base manipulation, CGI scripting, working with the World
Wide Web, and more. We'll discuss these in more detail in Part III.
|
|
Adding Libraries - The CPAN
|
|
Many shall run to and fro,
and knowledge shall be increased.
|
|
Bible, Daniel 12:4
|
|
The MacPerl distribution contains several hundred libraries and modules,
with more being added in each new release. These represent the most popu-
lar, powerful, and tested add-on functionality available for Perl. Even so,
there may be things you want to do with Perl for which no module has been
provided in the standard library.
|
|
Before you resign yourself to writing the code from scratch, take some time
to look farther afield. The Comprehensive Perl Archive Network (CPAN),
is a vast online repository of Perl code and documentation, contributed by
Perl programmers worldwide. The CPAN is available on the world-wide
web via www.perl.com/CPAN. We've also included a recent snapshot on
the CD-ROM that accompanies this book.
|
|
This is how the CPAN describes itself in CPAN.html, the "front-end" docu-
ment for the CPAN. CPAN.htmldescribes the CPAN, provides pointers to
available modules, and explains how you can contribute modules yourself.
|
|
The CPAN contains the collected wisdom of the entire Perl community:
hundreds of Perl utilities, several books' worth of documentation, and
|
|
|
|
7If you plan to make many calls to package functions, prefixing each name could get
tedious. Fortunately, you can bring an entire package into the current namespaceof
your programby using the packagedeclaration at the beginning of your script, as:
package MacPerl;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
the entire Perl distribution. If it's written in Perl, and it's helpful and
free, it's in the CPAN.
|
|
The CPAN currently lists 22 module categories of modules, including:
|
|
-
Perl Core Modules
-
Development Support
-
Networking, Devices, and Interprocess Communication
-
Data Type Utilities
-
Database Interfaces
-
User Interfaces
-
String, Language, and Text Processing
-
World Wide Web, HTML, HTTP, and CGI
-
Images, Pixmap, and Bitmap Manipulation
|
|
... and much more.
|
|
Not every module will work with MacPerl. Some modules are operating-
system specific. Others may call functions that MacPerl does not support, or
may depend upon your having previously installed some other module. The
ease with which a given module can be unpacked, installed, and used with
MacPerl varies widely. When you're ready to go looking for new modules,
be sure to review Chapter 11, Extensibility and Re-use, for hints and step by
step instructions for importing and installing new modules.
|
|
Mac-specific Functions
|
|
Because MacPerl runs on Mac OS, it has some different requirements than do
Perl versions for other platforms. Some of these requirements have to do
with traversing the filesystem, dealing with files, inter-application com-
munication (i.e., AppleEvents), etc. The MacPerl package was created to
handle these special requirements, adding functionality that would not be
needed on other platforms.
|
|
In fact, the functions in the MacPerl package are considered so much a part
of MacPerl that they almost seem built-in. This is the only package which
you do not need to specifically usein order to access its contents. MacPerl's
special functions are accessible just like any Perl function except for one
thing: you must still be sure to refer to these items by using the double-colon
syntax (prefixing each name with MacPerl::).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Aside from the MacPerl package of generally useful Mac OS-related func-
tions, there is also a set of more than a dozen modules, known collectively
as the toolbox modules. The toolbox modules access the Mac OS Toolbox, a
set of instructions for controlling various Mac OS functions. Mac OS has
thousands of toolbox calls; these modules attempt to provide a useful subset
of the calls in MacPerl.
|
|
The toolbox modules cover more in-depth Mac OS programming needs, from
getting file information to process control, interapplication communication,
graphics, speech recognition, and more. To access any of the toolbox mod-
ules, you must first specify its package with the appropriate usedirective.
Because of the way the toolbox modules are written, it is not necessary to
specify Mac::before each function name.8
|
|
The MacPerl package and the toolbox modules are very important to Mac-
Perl, providing fine control of many Mac OS features. For this reason, we
have devoted two entire chapters to them! We'll put off a detailed discus-
sion of the available Mac OS-specific functions to those chapters. Here,
however, are two simple examples to whet your interest.
|
|
The following example uses the function MacPerl::SetFileInfoto set
the Type and Creator IDs for an existing file, myscript(specified by its
full pathname). McPLis the Creator ID for a MacPerl script (plain text).
|
|
MacPerl::SetFileInfo('McPL', 'TEXT', 'HD:myscript');
|
|
The next example uses the FSPCreatefunction, part of the Mac::Files
module, to perform a similar function. This code creates a new (empty) file,
HD:newfile, with the specified Creator and Type.
|
|
use Mac::Files;
FSpCreate('HD:newfile', 'McPL', 'TEXT');
|
|
Droplets, Runtime Versions, And CGI scripts
|
|
So far, we've created and run all of our scripts from within MacPerl. But
there are alternative ways to save and run MacPerl scripts as "double-
clickable applications": droplets, runtime versions, and CGI scripts.
|
|
|
|
8Symbols in a module (e.g., functions, constants) can be explicitly exported through a
combination of defining them as part of the @EXPORTarray and calling the Exporter
module. These exported symbols become part of the namespaceof the calling package.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Each of these can be opened and edited from within the MacPerl appli-
cation, but they are no longer of type TEXTand cannot be edited with text
editors such as BBEdit or Alpha.9If you use an alternate text editor to
write and edit MacPerl scripts, we recommend that you keep your source
code in plain text form, and use Save As... to save a copy of the script as an
alternative type.
|
|
Droplets
|
|
Dropletsfunction as "mini" applications. They can be double-clicked in the
Finder, or you can drag and drop files and folders onto them. A droplet is a
MacPerl script which contains a small amount of additional code (causing
MacPerl to run the droplet script).10A droplet still requires you to have the
complete MacPerl distribution installed on your disk, but it can make run-
ning MacPerl scripts much simpler.
|
|
Runtime Versions
|
|
A runtime version of a MacPerl script contains a complete copy of MacPerl,
everything necessary to run the script. Consequently, a runtime version can
be very large; even a small script will be over 1 MB in size! A runtime ver-
sion isn't a very efficient way to save your own scripts, but it's a useful way
to send scripts to friends and co-workers who do not have the MacPerl
distribution installed (and may not want to).
|
|
CGI Scripts
|
|
MacPerl CGI scripts contains a small amount of code to allowthem to inter-
act with the Common Gateway Interface ( www.w3.org/CGI/), without
needing to understand the (very different) CGI AppleEvent protocol that is
used by Mac OS HTTP daemons.11See Chapter 16, CGI Scripting, for more
information.
|
|
|
|
9The type of these scripts is APPLand the creator ID is MrPl.The text of the script is
stored in a TEXTresource in the resource fork of the file, rather than in its data fork.
10The additional code in a droplet translates the "Open Document" AppleEvent sent
by the Finder into a "Do Script" AppleEvent for MacPerl. More abstractly, it receives
the Finder notification of which files, folders, etc. were dropped on it and executes the
script of the droplet with the path names of these dropped items in the @ARGVarray.
11This code translates the CGI Apple Events sent by Mac OS HTTP daemons into "Do
Script" Apple Events for MacPerl, by copying the appropriate Apple Event parameters
into the @ARGVand %ENVvariables and the standard input and output stream.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Program Arguments: @ARGV
|
|
When a Perl script is run, any arguments to the script are automatically
placed in the @ARGV(argument vector) array.12MacPerl scripts that are run
with the Run Scriptmenu item cannot have arguments, but droplets and run-
time versions can.
|
|
The following code fragment loops through the @ARGVarray and prints out
what it finds.13
|
|
for ($i = 0; $i <= $#ARGV; $i++) {
printf("item %d is %s\n", $i, $ARGV[$i]);
}
|
|
The arguments to a MacPerl script will usually be files or folders to open.14
What if you simply double-click a droplet or runtime version of a MacPerl
script that expects arguments, that is, a script that expects to have some-
thing dropped on it?
|
|
The following code uses the MacPerl::DoAppleScriptfunction to put up
a dialog box, asking the user the choose a (text) file to open.15
|
|
if ($#ARGV < 0) {
$file = MacPerl::DoAppleScript(<<SCRIPT);
choose file with prompt "Pick a file" ¬
of type "TEXT"
SCRIPT
exit(1) if ($file eq '');
$file =~ s/^alias "//;
$file =~ s/"$//;
push(@ARGV, $file);
}
print("you picked: $ARGV[0]\n");
|
|
|
|
12The name, ARGV, is taken from the argvarray in the Cprogramming language.
13This fragment is part of the drplt.tst.pd droplet example included on the CD-ROM.
14Actually, you can send just about any sort of information as an argument, provided
you encode that information as the name of a file or folder. But most often, arguments
are simply ordinary files that contain data or folders containing files (or more folders).
15If you want to "break" and continue a long line of AppleScript code across more
than one line of Perl, end each unfininished partial line of AppleScript code with the
AppleScript continuation character, ¬, (formed by the sequence option-L).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Note the nifty use of the here document to send the AppleScript code to Mac
Perl::DoAppleScript. (The characters that follow SCRIPTon the ini-
tial line are not passed to DoAppleScript.)
|
|
The two substitution commands
|
|
$file =~ s/^alias "//;
$file =~ s/"$//;
|
|
are needed because AppleScript returns the name of the chosen file as
|
|
alias "HD:examples:chapter8.pl"
|
|
We use push()to add the name of the chosen file into @ARGV. The push()
function pushes the values in a list (in this case, the single element, $file)
onto the end of an array. The length of the array is increased by the length
of the list.
|
|
The next example does essentially the same thing, this time using the Stan
dardGetFilefunction from one of the toolbox modules, Mac::Standard
file, to put up the dialog box. Mac::Standardfileis described in detail
in chapter 13, The Toolbox Modules.
|
|
if ($#ARGV < 0) {
use Mac::StandardFile;
$file = StandardGetFile('', 'TEXT');
if ($file->sfGood()) {
push(@ARGV, $file->sfFile());
} else {
exit(1);
}
}
print("you picked: $ARGV[0]\n");
|
|
References
|
|
Just as HFS (the Mac OS Hierarchical File System) has "aliases" that can
be used in place of files and folders, Perl has "references" that can be used in
place of variables, functions, etc. Perl's references are a bit too complicated
to explain fully here, but a brief introduction should be useful.
|
|
Perl supports two forms of references: symbolicand hard. A symbolic refer-
ence is implemented in a manner that is very analogous to HFS aliases (or
Unix symbolic links). Specifically, a symbolic reference is a string variable
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
that contains the name of another variable. In the following example, Perl
"dereferences" $$ref(getting $var), then increments the result:
|
|
$var = 1;
$ref = "var";
$$ref++;
print("ref=<$ref>, var=<$var>\n");
|
|
Displays:
|
|
ref=<var>, var=<2>
|
|
A hard reference, in contrast, is implemented in a manner that is analogous
to a Unix hard link.16That is, it refers to the entity (e.g., function, number,
string) itself,17rather than to the nameof the entity. This saves Perl the
time needed to "look up" the entity; more critically, it allows the reference
to act differently in some cases.
|
|
Because a hard reference is a scalar, it can be stored in a variable (including
an array or hash location). It is neither a number nor a string, however, so it
cannot be operated upon. Here is the same example, using a hard reference:
|
|
$var = 1;
$ref = \$var;
$$ref++;
print("ref=<$ref>, var=<$var>\n");
|
|
Displays:
|
|
ref=<SCALAR(0x87de08)>, var=<2>
|
|
As shown above, the hard reference $ref does not have a value that is
likely to be meaningful to a human reader. Nonetheless, it works quite
well! Perl's "reference" operator, used above, is a backslash, \.
|
|
Because references can be made to functions, you can actually "store" func-
tions in a hash. If you were building a lookup table for an interpreter, for
instance, you might pre-load (and then use) its table of functions in a man-
ner something like this:
|
|
|
|
16Unfortunately, there is no Mac OS analog for hard links.
17Programming Perlrefers to this entity as a "thingy" or, grudgingly, as a "referent".
You may prefer to think of it as the Ding an sich(the thing itself).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$funcs{'foo'} = \&foo;
...
$out = &{ $funcs{$func_name} } ($in);
|
|
Note:Perl's built-in functions cannot be referenced and invoked in this
manner. So, if need be, create a "wrapper" function:
|
|
sub my_sqrt { sqrt(@_[0]); }
|
|
Filename Globbing
|
|
If you have ever used a command line shellsuch as MPW or one of the Unix
shells, you may be familiar with wild card characters, also known as shell
metacharacters, which are used to match simple filename patterns. For
example, in the MPW shell one might specify all of the filenames that end
in the suffix .plas
.pl. Under Unix, the same set of filenames would be
specified as *.pl.
|
|
When you generate filenames in this manner, using wildcards, you are
globbingthe filenames.18Perl also allows filename globbing, using the
standard Unix (C shell) metacharacters in specifying globbing patterns:
|
|
*match any number of characters (except leading dot, .)
?match any single character (except leading dot, .)
[...]match any of the characters in the class
{...,...}match any of the comma-separated alternatives in the group
|
|
In MacPerl, only *and ?are supported. Filename globbing under Perl is
specified by describing a glob patternwithin the filename globbing opera-
tor, <>, the glob()function, or (MacPerl only), the `glob`command.19
|
|
As an example, suppose you have several files with names ending in .pl.
From a MacPerl script, you could specify only these files in several ways:
|
|
@files = <*.pl>;
foreach $name (@files) { print "$name\n"; }
|
|
|
|
18In Version 6 Unix, this task was performed by the /etc/globprogram.
19Recall that the <>(angle) operator is used to read input from a filehandle. If the
string within the angle brackets is anything other thana filehandle (or a scalar varia-
ble that can evaluate to a filehandle), then the <>operator becomes the globbing opera-
tor instead. TMTOWTDI.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@files = glob('*.pl');
foreach $name (@files) { print "$name\n"; }
|
|
@files = `glob *.pl`;
foreach $name (@files) { print "$name\n"; }
|
|
The first two globs, <*.pl>, and glob('*.pl')print the same results, e.g.
|
|
chapter6.pl
chapter7.pl
...
|
|
The backquoted glob command, e.g. `glob *.pl`includes its own embed-
ded newline, resulting in slightly different values for each $name
|
|
chapter6.pl
|
|
chapter7.pl
|
|
...
|
|
Passing Filehandles
|
|
A typeglobis a way of accessing the internal data type that Perl uses to
hold an entire symbol table entry.20A typeglob represents all data types
that share a given name. That is, the typeglob *fooaffects the entities
$foo, @foo, &foo, etc.
|
|
Prior to version 5 of Perl, typeglobs were used to pass references to arrays
and hashes to functions. Weird, but useful. With the advent of real refer-
ences, however, this is no longer necessary.
|
|
Typeglobs can also be used to alias one set of names to another:
|
|
*abc = *def;
|
|
This ties $abcto $def, @abcto @def, &abcto &def, etc. It is not clear why
one would wish to do this; perhaps it's just another Way To Do It ...
|
|
|
|
20A typeglobhas only a passing relationship to a filename glob: both use the same
root word, glob, and both use the same wildcard *character (which "globs" things
together). A typeglob globs symbols of the same name from the symbol table. A filename
glob globs filenames that match a specified pattern. (Yes, we know it's confusing!)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Typeglobs are still useful, however. You can pass a filehandle reference to a
subroutine, store it in a data structure, etc. (Because filehandles themselves
cannot be stored or passed, this is quite a useful characteristic.)
|
|
The precise method Perl uses to do this is, unfortunately, more mysterious
than we have any interest in explaining (see Programming Perl). Nonethe-
less, we cantell you how to use typeglobs for this purpose, and we will.
|
|
Typeglobs are prefixed by an asterisk, *. References are, as always, pre-
fixed by a backslash, \. To save a reference to a filehandle in a scalar
variable, you actually save a reference to a typeglob of the filehandle:
|
|
$fh = \*FH;
|
|
You can treat a reference to a filehandle like any other reference, as:
|
|
$fh{'abc'} = \*FH;
$fh[12345] = \*FH;
|
|
Now, let's get some real use out of one! Here's a simple function that will
print its argument string to a specified (pre-opened) file, preceded by the
current time. This might be useful if you needed to maintain several log
files. To get the time information, we'll use our hms()subroutine.
|
|
sub log_it {
my($string, $fh) = @_;
|
|
$the_time = hms();
print $fh ("$the_time\t$string");21
}
|
|
You might call log_it()in any of several ways, changing the filehandle
that describes where to print the output.
|
|
$log = "logfile";
open(LOG, ">HD:$log") or die "Cannot create $log\n";
...
log_it("Begin Processing\n", \*LOG);
...
log_it("Started\n", \*STDERR);
...
|
|
|
|
21Note that only one dollar sign is used with the filehandle reference $fh; that is, no
explicit dereferencing is done. Don't ask ...
|
Copyright © 1997-1998 by Prime Time Freeware. All Rights Reserved.