Function Footnotes

Now that you know about scope, there are some things that can only be done effectively with scope. One is recursive functions, and the other is the use strict Perl statement that enables a stricter Perl, possibly preventing you from making mistakes.

Declaring Variables local

Perl version 4 didn't really have "private" variables. Instead, Perl 4 had variables that were "almost" private. This concept of "almost" private variables is still around in Perl 5. You declare these variables by using the local operator, as in this example:

sub myfunc {
    local($foo)=56;
    # rest of function…
}

In the preceding snippet, $foo is declared to be local to the myfunc() subroutine. A variable that has been declared with local acts almost identically to one declared with my: It can be scoped to a subroutine, block, or eval, and its value is destroyed upon leaving the subroutine or block.

The difference is that a variable declared local can be seen within its scoped block and within any subroutines called from that block. Table 8.1 shows a side-by-side comparison.

Table 8.1. Comparing my and local

sub mess_with_foo {

    $foo=0;
}

sub myfunc {

    my $foo=20;

    mess_with_foo();

    print $foo;

}

myfunc();

sub mess_with_foo {

    $foo=0;

}

sub myfunc {

    local $foo=20;

    mess_with_foo();

    print $foo;

}

myfunc();

The two snippets shown in this table are identical except for the declaration of $foo in myfunc(). On the left, it's declared with my; on the right, with local.

When the code on the left runs, $foo is created private to myfunc(). When mess_with_foo() is called, the $foo that's changed inside mess_with_foo() is the global variable $foo. When control returns to myfunc(), the value of 20 is printed because the $foo in myfunc() was never changed.

When the code on the right runs, $foo is created and declared local to myfunc(). When mess_with_foo() is called, $foo is set to 0. That $foo is the same $foo from myfunc(); the "privateness" is passed down to the called subroutine. Upon returning to myfunc(), the value of 0 is printed.

Note

If you're picky about terminology, local variables are technically known as dynamically scoped variables because their scope changes depending on what subroutines are called. Variables declared with my are called lexically scoped variables because their scope can be determined by simply reading the code and noting what block they were declared in, and the scope does not change.


Whenever your program needs a variable that's private to a subroutine, you almost always want a variable declared with my.

Making a Stricter Perl

Perl is a permissive language. It doesn't try to get in your way; it allows you to just get your work done without complaining too much about what your code looks like. You also can tell Perl to be a bit more strict about your code. For example, Perl can help you avoid silly mistakes if you use the warning switch on the command line—or on the #! line. Perl warns you when you're using undefined variables, using a variable name only once, and so on.

In large software projects and as your own programs get larger and larger, it's nice to have Perl help you keep yourself in line. In addition to using the -w switch, you can tell the Perl interpreter at compile time to turn on more warnings. You do so by using use strict:

use strict;
sub mysub {
    my $x;
    :
}
mysub();

The use strict statement is actually something called a compiler directive. It tells Perl to flag the following situations as runtime errors that point onward in the current block or file:

  • Attempts to use variable names (other than special variables) that are not declared with my

  • Attempts to use a bare word as a function name when the function definition hasn't been seen yet

  • Other potential errors

The use strict directive, for now, helps you avoid the first two problems. Having Perl flag variables not declared with my prevents you from using a global variable when you actually intend to use a private variable; it's a way of helping you write more self-contained code and not rely on global variables.

The last trap that use strict catches is that of bare keywords. Examine this code:

$var=value;

In this case, do you mean for value to be interpreted as a function call or as a string (but you forgot the quotes)? Perl's use strict directive would note that this code is ambiguous and disallow the syntax, unless the subroutine value was already declared before this statement was reached.

From this point on, I will include use strict in all the exercises and longer program listings in this book.

Recursion

You will probably run into a special class of subroutines sooner or later. These subroutines actually call themselves in order to do their work. They are called recursive subroutines.

Recursive subroutines are used wherever tasks can be broken down into smaller and smaller identical tasks. One recursive task is searching a directory tree for a file. After searching the topmost directory, as subdirectories are found, those directories must be searched. And within those directories, if subdirectories are found, those directories must be searched. You should begin to see a pattern here.

Another recursive task is computing factorials, which are frequently used in statistics. The number of ways the letters ABCDEF can be arranged is 6-factorial. Factorials are the product of a number and all the smaller numbers down to 1. The factorial of 6 is 6*5*4*3*2*1, or 720. To compute the factorial of 6, you must compute the factorial of 5 and multiply that by 6. To compute the factorial of 5, you must compute the factorial of 4 and multiply that by 5, and so on. A recursive function for computing factorials is shown in Listing 8.3.

Listing 8.3 Factorials
1:   sub factorial {
2:      my ($num)=@_;
3:      return(1) if ($num <= 1);
4:      return($num*factorial($num-1));
5:   }
6:   print factorial(6);

Line 2: The argument to the factorial() subroutine is copied to $num, which is declared private to the subroutine.

Line 3: Every recursive function needs to have a termination condition. That is, there needs to be some point at which the function can no longer call itself for an answer. For the factorial() subroutine, the termination condition is 1-factorial (or 0-factorial); those two values are both 1, and the factorial() subroutine will execute the return(1) when called with 1 or 0 ($num<=1).

Line 4: Otherwise, if the argument is not 0 or 1, the factorial of the next smaller sequence must be computed. Observe the following:

If $num is:Line #4 evaluates as follows:
6return(6 * factorial(5))
5return(5 * factorial(4))
4return(4 * factorial(3))
3return(3 * factorial(2))
2return(2 * factorial(1))
1Line #4 is not reached; factorial(1) returns 1.

After the next-smaller factorials are computed (all the way down to 1), the function starts returning values up through the chain of function calls until finally 6-factorial can be computed.

Recursive functions aren't very common: Large ones are complicated to construct and difficult to debug. Any task that can be done through iteration (using for, while, foreach) can be done with recursion, and any recursive task can be accomplished iteratively. Recursion is usually reserved for a small set of tasks that lend themselves to it easily.

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

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