Going Deeper

I've covered a lot of ground in this chapter, from packages to modules to importing, to how Perl actually looks at code at various times. Much of what I've discussed in this chapter is the tip of the iceberg. Packages and modules, in particular, could fill up entire books of their own, and the short description of object orientation is barely even enough to get started.

We'll go a little deeper into a few of these topics later in this book. Other topics, however, including developing your own packages and just about everything to do with creating modules, are best left to the advanced Perl programmer, and as such are outside the scope of this book. After finishing the 21 days of this course, consider exploring modules and packages with the text in the online man pages and FAQs.

There are a few topics that I can describe in this section, however, that relate more closely to the topics we've discussed in this lesson.

Typeglobs

Typeglobs is a strange name that involves being able to refer to multiple types of variables by a single name (the term typeglob is borrowed from Unix, where referring to multiple files using file.* or some such character is called file globbing). Typeglobbing refers to the symbol table entries of a given package.

The typeglob expression *foo refers to any variable with the name foo$foo, @foo, &foo, and so on. Normally, these variables are distinct; typeglobbing is a way of lumping them all together.

In earlier versions of Perl, typeglobbing was used to pass arrays into subroutines by reference. You could use a typeglob to alias a local variable to a global array, and then any changes you made to the local array would be reflected outside the global array, like this:

@foo = (1,2,3);
&removethrees(*foo);

sub removethrees {
   my *list = @_;
   foreach my $x @list {
      if ($x == 3) { undef $x }
   }
   return @list;
}

In this example, all the changes made to the local list in @list are reflected in the list @foo, because @list has been set up as an alias for @foo.

Typeglobs for passing arrays into subroutines have been essentially replaced by the newer reference feature; references not only enable you to pass individual arrays into subroutines by reference, but also enable you to maintain the integrity of multiple arrays. Use references instead of typeglobs for this purpose (more about references on Day 19).

One Other Difference Between local and my

The most obvious difference between variables defined with my and those defined with local is that of lexical and dynamic scope, as I noted in the body of this lesson. The other difference is how Perl manages those variables:

  • local local variables are actually global variables in disguise. When you create a local variable, if a global of the same name exists, Perl saves the value of that global and reinitializes the same variable (and its same location in the symbol table) to the new local value. The value of the global is restored at the end of the local variable's scope.

  • my local variables are entirely new variables that are not stored in the symbol table. They are wholly private to the block or subroutine in which they're stored. This makes them slightly faster to use than local local variables because they don't require a symbol table lookup.

Neither of these differences really change how you would use local or my in your own scripts (local local variables are relevant when it comes to using typeglobs, but that's a detail outside the scope of this book). In most cases, use my where you want a local variable and you'll do just fine.

An Example of local Versus my

In the section on local variables defined using either local or my, I promised an example of how this works if the difference was totally befuddling. Listing 13.3 shows a script to make things clearer (or perhaps just completely dark).

Listing 13.3. A Scope Script
1:  #!/usr/ bin/perl -w
2:
3:  $global = " global available here
";
4:
5:  &subA();
6:  print "Main script:
";
7:  foreach $var ($global, $mylocal, $locallocal) {
8:      if (defined $var) {
9:          print $var;
10:     }
11: }
12:
13: sub subA {
14:     my $mylocal = " mylocal available here
";
15:     local $locallocal = " local local available here
";
16:     print "SubA:
";
17:     foreach $var ($global, $mylocal, $locallocal) {
18:         if (defined $var) {
19:             print $var;
20:         }
21:     }
22:     &subB();
23: }
24:
25: sub subB {
26:     print "SubB: 
";
27:     foreach $var ($global, $mylocal, $locallocal) {
28:         if (defined $var) {
29:             print $var;
30:         }
31:     }
32: }
						

This script uses three variables: $global, $mylocal, and $locallocal, and declares them all appropriately. For each subroutine, and then once again at the end of the script, the values of those variables are printed if they exist and if they have a proper value. Try to follow the flow of this script and predict what will get printed when.

Here's the actual output:

SubA:
 global available here
 mylocal available here
 local local available here
SubB:
 global available here
 local local available here
Main script:
 global available here

Was it what you expected? Let's work through it. This script starts at the top and calls &SubA(), which calls &SubB(), and then prints some variables. In the subroutine &SubA(), we declare both $mylocal and $locallocal with the my and local modifiers, respectively, in lines 14 and 15. Both those variables, plus the $global, will then be available inside the boundaries of that subroutine, so all three values will be printed.

In line 22, &subA() calls &SubB(), and here we just print the variables that we have available. The global will be there because global variables are available to all parts of a script. But $mylocal is not there—the my modifier makes that local variable only available to that subroutine and not to other parts of the script. The $locallocal variable definition, however, is available to subroutines further down than the one in which it was defined.

After &SubB() is finished, execution pops back up to &subA(), and then back up to the main part of the script where we try printing those same values again. Here only the global will be available, and only the global will get printed.

Package Initialization and Finalization with BEGIN and END

One aspect of packages worth mentioning before we move on are the subroutines BEGIN and END, which are used to initialize a script before it runs, and to finalize a script after it's done running. These subroutines are most commonly used inside complex Perl libraries and modules, and as constructors and destructors for object-oriented classes.

The BEGIN subroutine is executed just as soon as it's found, during compile time, before the rest of the script is parsed. Use BEGIN for any code that you want to run at compile-time, for example, importing symbols for module definitions that will later be exported to other code, or to include other code that is required by the module.

END, on the other hand, is executed as the Perl script finishes executing, either if the script executed correctly or if there was an error that caused it to execute prematurely (including die). Use END to clean up after your script. You can change the status value that is returned to the Unix shell after your script exits using END, for example.

You can find out more about BEGIN and END in the perlmod man page.

Importing Code with require

In this lesson, I showed you how to import code from modules using the use function. The use function, in short, imports code from another source at compile time, and imports various symbols into the current package. The require function, on the other hand, is used to include code from other sources at runtime (in fact, use is equivalent to calling require inside a BEGIN subroutine and importing that file's variables into the current namespace).

In earlier versions of Perl, require was used as the general-purpose import mechanism. You stuck your subroutine or global variable definitions in a separate file, and then included that file in another script using require, like this:

require 'foo.pl';

Perl looks for the given file to be imported in the directories stored in @INC. In addition, it keeps track of which files have already been imported, so it won't reimport code that's already been loaded. If the included file, however, defines its own package, then require will not import that package's variables into the current package (not even main), and you'll have to refer to those variables using the complete package name. Also, you have to be careful when you use require; because the require occurs at runtime, you must make sure the require happens before you actually call or use anything defined in that imported file.

This mechanism for importing code from one file to another still works just fine in current versions of Perl, and you can use it to build your own simple libraries of subroutine definitions. However, the new mechanisms of packages and modules provide better features and more control over what gets imported when. For serious library development, consider learning more about the development packages, modules, and use.

One other cool use of require is with a Perl version number, in which case if the script is being run with an earlier version of Perl, it will immediately exit with an error. Use this mechanism for making sure the version of Perl that's being run has the features you use in your script—for example, features that only exist in Perl 5, or more advanced features that might only exist in 5.005 or higher:

require 5.005;

See the perlfunc man page for further information on require.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.156.122