ЭЛЕКТРОННАЯ БИБЛИОТЕКА КОАПП
Сборники Художественной, Технической, Справочной, Английской, Нормативной, Исторической, и др. литературы.



3.2 Typeglobs

Typeglobs, we mentioned earlier, can be localized (with local only) and assigned to one another. Assigning a typeglob has the effect of aliasing one identifier name to another. Consider

$spud   = "Wow!";
@spud   = ("idaho", "russet");
*potato = *spud;   # Alias potato to spud using typeglob assignment
print "$potato\n"; # prints "Wow!"
print @potato, "\n"; # prints "idaho russet"

Once the typeglob assignment is made, all entities that were called "spud" can now also be referred to as "potato" - the names are freely interchangeable. That is, $spud and $potato are the same thing, and so are the subroutines &spud and &potato. Figure 3.2 shows the picture after a typeglob assignment; both entries in the symbol table end up pointing to the same typeglob value.[3]

[3] There is a wee bit of simplification here, which we will clarify in Chapter 20.

Figure 3.2: Assigning *spud to *potato: both symbol table entries point to the same typeglob

Figure 3.2

The alias holds true until the typeglob is reassigned or removed. (We will shortly see how to remove a typeglob.) In the example, there is no subroutine called spud, but if we define it after the typeglobs have been assigned, that subroutine can also be invoked as potato. It turns out that the alias works the other way too. If you assign a new list to @potato, it will also be automatically accessible as @spud.

3.2.1 Temporary Aliases

For now, there is no easy, intuitive way to get rid of an alias created by a typeglob assignment (you may reassign it, of course). You can, however, get temporary aliases using local, because it restores the typeglob's values at the end of the block.

Consider

$b = 10;
{
    local *b;    # Save *b's values
    *b = *a;     # Alias b to a
    $b = 20;     # Same as modifying $a instead
}                # *b restored at end of block
print $a;        # prints "20"
print $b;        # prints "10"

local *b localizes all changes to variables named "b"; that is, it puts all of *b's value pointers into safekeeping and substitutes an undef value for all of them. This lasts until the end of the block, whereupon the previous values of everything named "b" are restored ($b becomes 10 again). Now, because of the alias (*b = *a), the assignment $b = 20 has the effect of modifying both $a and $b. But at the end of the block, only $b is restored, and $a is left with the new value.

While we are on the subject, it is important to recall that lexical variables and the symbol table have nothing to do with each other; for this reason, localizing a typeglob with my is a compile-time error:

my(*F);

The script dies with this error: "Can't declare ref-to-glob cast in my."

3.2.2 Using Typeglob Aliases

This section discusses a number of places where typeglob aliases work very well.

3.2.2.1 Efficient parameter passing

Aliases happen to be quite a bit faster than references, because they don't need to do any dereferencing. Consider

$a = 10;
*b = *a ; $b++ ;  # 1. Increment $a indirectly through the typeglob
$r = \$a; $$r++;  # 2. Increment $a indirectly through the reference

Case 1 is around one and a half times faster than case 2 on my PC.

The example code below uses typeglobs to efficiently pass an array by reference to a subroutine, DoubleEachEntry, which doubles each element of the array:

@array = (10,20);
DoubleEachEntry(*array); # @array and @copy are identical.
print "@array \n"; # prints 20 40

sub DoubleEachEntry {
    # $_[0] contains *array
    local *copy = shift;  # Create a local alias
    foreach $element (@copy) {
        $element *= 2;
    }
}

Note that only one parameter is passed to the subroutine. The typeglob *copy springs into existence when it is first encountered, but because it didn't exist prior to the local statement, it and the corresponding entry in the symbol table are removed at the end of the block.

Incidentally, the code also takes advantage of the fact that the foreach statement internally aliases each successive element of @copy to $element, so modifying $element affects the elements of @copy (and therefore @array as well).

You cannot use a lexically scoped array as a parameter to DoubleEachEntry, because lexical variables don't have typeglobs associated with them. The restriction is easily circumvented, however. Typeglobs and references are strangely equivalent, as it turns out. You can pass in an ordinary reference to a subroutine expecting a typeglob, and it'll work well. (We'll have a little more to say about this in the section "Typeglobs and References.") That is, you can pass lexically scoped arrays to DoubleEachEntry like this:

my @array  = (1, 2, 3);
DoubleEachEntry(\@array); # Instead of *array, which wouldn't work

3.2.2.2 Aliasing on command lines

I often embed the Perl interpreter in my C/C++ programs to provide a powerful shell-like frontend. While I like to use long and descriptive subroutine names in Perl scripts, it is painful to keep typing them on the frontend's prompt. Aliases are very useful here:

sub a_long_drawn_out_sub_name {
   print "A sub by any other name is still a sub \n";
}
*f = *a_long_drawn_out_sub_name; # create an alias 

Now typing f() on the command line is the same as calling the original subroutine but much easier on the carpals!

3.2.2.3 Friendly predefined variables with aliases

Consider the opposite case. Perl has a number of cryptic built-in variables such as $!, $/, and $@ , and many people would much rather work with longer descriptive names. The module English.pm in the standard Perl library comes to the rescue; it provides nice big, long aliased names, such as $ERRNO, $INPUT_RECORD_SEPARATOR, and $EVAL_ERROR (respectively). Try this:

use English;    # Import the module file called English.pm
# Try deleting a non-existent file
unlink ('/tmp/foo');
if ($ERRNO)  {  # Use $ERRNO instead of $!
    print $ERRNO, "\n"; # Prints "No such file or directory"
}

(We'll cover packages and the use statement in Chapter 6.) I think these well-known names should have been in there from the very beginning, instead of having to memorize funny-looking variables and an accompanying list of mnemonics. Some argue that you can use the same scheme for other languages ("use Dutch;"), but considering that the other system calls are in English anyway, I think that there's no point providing specific aliases for a small subset of the things you have to remember.

3.2.3 Aliasing Problem: Variable Suicide

Aliases, combined with the fact that local doesn't really create new variables (it temporarily slaps a new value onto a global variable), often leads to weird values of variables that presumably haven't even been touched. Consider the following simple case:

$x = 10;
foo(*x);
sub foo {
    local(*y) = @_;
    print "Before value of y : $y \n";
    local($x) = 100;
    print "After value of y : $y \n";
}

This prints the following:

Before value of y : 10
After value of y : 100

Can you resolve the mystery? Clearly, $y has not been touched between the two print statements, but its value seems to have changed. Hint: it reflects that of $x.

Let's trace the events in sequence:

$x = 10;                  # Assign a value to global $x
                          # function called
local *y = *x;            # Save global *y's values. Alias it to *x
print "before value"      # Because of the alias, $y is the same as $x,
                          # hence this prints 10
local $x = 100;           # IMPORTANT: local saves $x's value (10)
                          # and substitutes 100. Note that it does not
                          # create a new $x variable
                          # replaced by 100
print "after value";      # But *y is still aliased to *x. Therefore,
                          # $y now contains 100

The interaction of aliases and local can be even more subtle. Consider

foreach $f (10, 20, 30) {
    foo (*f);
}
sub foo {
    local (*g) = @_;
    $g++;
}

This prints the error: "Modification of a read-only value attempted at try.pl line 6."

The sequence is as follows: For efficiency, the foreach operator aliases $f to the next element of the list in every iteration, each of which is a constant. The subroutine foo aliases *g to *f, which means that $g is aliased to a constant. For this reason, the operation $g++ causes a problem.

Moral of the story: if you want truly local variables, use my. Use typeglob aliasing and local sparingly.


Previous: 3.1 Perl Variables, Symbol Table, and ScopingAdvanced Perl ProgrammingNext: 3.3 Typeglobs and References
3.1 Perl Variables, Symbol Table, and ScopingBook Index3.3 Typeglobs and References