| |||
Links Sections Chapters Part I: Basic Perl 02-Numeric and String
Literals Part II: Intermediate Perl Part III: Advanced Perl 13-Handling Errors and
Signals Part IV: Perl and the Internet 21-Using Perl with Web
Servers Appendixes |
This chapter takes a look at functions. Functions are blocks of codes that are given names so that you can use them as needed. Functions help you to organize your code into pieces that are easy to understand and work with. They let you build your program step by step, testing the code along the way.
After you get the idea for a program, you need to develop a program outline - either in your head or on paper. Each step in the outline might be one function in your program. This is called modular programming. Modular programming is very good at allowing you to hide the details so that readers of your source code can understand the overall aim of your program.
For instance, if your program has a function that calculates the area of a
circle the following line of code might be used to call it: $areaOfFirstCircle = areaOfCircle($firstRadius);
By looking at the
function call, the reader knows what the program is doing. Detailed
understanding of the actual function is not needed.
Tip |
Well thought out function and variable names help
people to understand your program. If the line of code was
$areaFC = areaCirc($fRad);Its meaning would not be as clear. |
Note |
Calling a function means that Perl stops executing
the current series of program lines. Program flow jumps into the program
code inside the function. When the function is finished, Perl jumps back
to the point at which the function call was made. Program execution
continues from that point onward. |
Let's look at the function call a little closer. The first thing on the line is a scalar variable and an assignment operator. You already know this means Perl assigns the value on the right of the assignment operator to $areaOfFirstCircle. But what exactly is on the right?
The first thing you see is the function name: areaOfCircle(). The parentheses directly to the right and no $, @, or % beginning the name indicates that this is a function call. Inside the parentheses is a list of parameters or values that get passed to the function. You can think of a parameter just like a football. When passed, the receiver (for example, the function) has several options: run (modify it in some way), pass (call other routines), fumble (call the error handler).
Note |
Perl allows you to use the & character
to start function names, and in a few cases it is needed. Those few
situations that the & character is needed are beyond the
scope of this book. |
Listing 5.1 shows a short program that calls and defines the areaOfCircle() function.
Pseudocode |
Assign $areaOfFirstCircle the value that is returned by the function areaOfCircle(). Print $areaOfFirstCircle. Define the areaOfCircle() function. Get the first parameter from the @_ parameter array. Calculate the area and return the new value. |
Listing 5.1-05LST01.PL - Calculating the Area of a Circle |
|
This program prints: 78.5375
The fact that something prints tells you that the program
flow returned to the print line after calling the areaOfCircle()
function.
A function definition is very simple. It consists of: sub functionName {
}
That's it. Perl function definitions never get any more complex.
The complicated part comes when dealing with parameters. Parameters are values passed to the function (remember the football?). The parameters are specified inside the parentheses that immediately follow the function name. In Listing 5.1, the function call was areaOfCircle(5). There was only one parameter, the number 5. Even though there is only one parameter, Perl creates a parameter array for the function to use.
Inside the areaOfCircle() function, the parameter array is named
@_. All parameters specified during the function call are stored in the
@_ array so that the function can retrieve them. Our small function did
this with the line: $radius = $_[0];
This line of code assigns the first element of
the @_ array to the $radius scalar.
Note |
Because parameters are always passed as lists, Perl
functions are also referred to as list operators. And if only one
parameter is used, they are sometimes referred to as unary operators.
However, I'll continue to call them functions and leave the finer points
of distinction to others. |
The next line of the function: return(3.1415 * ($radius ** 2));
calculates the circle's area and
returns the newly calculated value. In this case, the returning value is
assigned to the $areaOfFirstCircle scalar variable.
Note |
If you prefer, you don't need to use the
return() function to return a value since Perl automatically
returns the value of the last expression evaluated. I prefer to use the
return() function and be explicit so that there is no mistaking
my intention. |
You may have used programming languages that distinguish between a function and a subroutine. The difference being that a function returns a value and a subroutine does not. Perl makes no such distinctions. Everything is a function - whether or not it returns a value.
Pseudocode |
Call the firstSub() function with a variety of parameters. Define the firstSub() function Assign $numParameters the number of elements in the array @_. Print out how any parameters were passed. |
firstSub(1, 2, 3, 4, 5, 6); firstSub(1..3); firstSub("A".."Z"); sub firstSub { $numParameters = @_; print("The number of parameters is $numParameters\n"); }This program prints out:
The number of parameters is 6 The number of parameters is 3 The number of parameters is 26Perl lets you pass any number of parameters to a function. The function decides which parameters to use and in what order. The @_ array is used like any other array.
Let's say that you want to use scalar variables to reference the parameters so that you don't have to use the clumsy and uninformative $_[0] array element notation. By using the assignment operator, we can assign array elements to scalars in one easy step.
Pseudocode |
Call the areaOfRectangle() function with varying parameters. Define the areaOfRectangle() function. Assign the first two elements of @_ to $height and $width respectively. Calculate the area. Print the three variables: $height, $width and $area. |
areaOfRectangle(2, 3); areaOfRectangle(5, 6); sub areaOfRectangle { ($height, $width) = @_; $area = $height * $width; print("The height is $height. The width is $width. The area is $area.\n\n"); }This program prints out:
The height is 2. The width is 3. The area is 6. The height is 5. The width is 6. The area is 30.The statement ($height, $width) = @_; does the array element to scalar assignment. The first element is assigned to $height and the second element is assigned to $width. After the assignment is made you can use the scalar variables to represent the parameters.
Pseudocode |
Create an array with 6 elements. Print the elements of the array. Call the firstSub() function. Print the elements of the array. Define the firstSub() function. Change the values of the first two elements of @_. |
Listing 5.2-05LST02.PL - Using the @_ Array to Show Call by Reference |
|
This program prints: Before function call, array = 0 1 2 3 4 5
After function call, array = A B 2 3 4 5
You can see that the
function was able to affect the @array variable in the main program.
Generally this is considered bad programming practice because it does not
isolate what the function does from the rest of the program. If you change the
function so that scalars are used inside the function this problem goes away.
Listing 5.3 shows how to redo the program in Listing 5.2 so that scalars are
used inside the function.
Pseudocode |
Create an array with 6 elements. Print the elements of the array. Call the firstSub() function. Print the elements of the array. Define the firstSub() function. Assign the first two elements of @_ to $firstVar and $secondVar.P> Change the values of the scalar variables. |
Listing 5.3-05LST03.PL - Using Scalars Instead of the @_ Array inside Functions |
|
This program prints: Before function call, array = 0 1 2 3 4 5
After function call, array = 0 1 2 3 4 5
This example shows that the
original @array variable is left untouched. However, another problem
has quietly arisen. Let's change the program a little so that the values of
$firstVar are printed before and after the function call. Listing 5.4
shows how changing a variable in the function affects the main program.
Pseudocode |
Assign a value to $firstVar. Create an array with 6 elements. Print the elements of the array. Call the firstSub() function. Print the elements of the array. Define the firstSub() function. Assign the first two elements of @_ to $firstVar and $secondVar. Change the values of the scalar variables. |
Listing 5.4-05LST04.PL - Using Variables in Functions Can Cause Unexpected Results |
|
This program prints: Before function call
firstVar = 10
array = 0 1 2 3 4 5
After function call
firstVar = A
array = 0 1 2 3 4 5
By using the $firstVar variable
in the function you also change its value in the main program. By default, all
Perl variables are accessible everywhere inside a program. This ability to
globally access variables can be a good thing at times. But it doesn't help when
trying to isolate a function from the rest of your program. The next section
shows you how to create variables that can only be used inside functions.
It is very useful to be able to limit a variable's scope to a single function. In other words, the variable will have a limited scope. This way, changes inside the function can't affect the main program in unexpected ways. Listing 5.5 introduces two of Perl's built-in functions that create variables of limited scope. The my() function creates a variable that only the current function can see. The local() function creates a variable visible inside the current function and inside any functions that are called by the current function. If that sounds confusing, don't worry. It is confusing, but Listing 5.5 should clear things up. In this case, it's a listing that is worth a thousand words, not a picture!
Pseudocode |
Call firstSub() with two parameters. Define the firstSub() function. Assign the first parameter to local variable $firstVar. Assign the second parameter to my variable $secondVar. Print the variables. Call the second function without any parameters. Print the variables to see what changed. Define the secondSub() function. Print the variables. Assign new values to the variables. Print the variables to see that the new values were assigned correctly. |
Listing 5.5-05LST05.PL - Using the Local and My Functions to Create Local Variables |
|
This program prints: firstSub: firstVar = AAAAA
firstSub: secondVar = BBBBB
secondSub: firstVar = AAAAA
Use of uninitialized value at test.pl line 19.
secondSub: secondVar =
secondSub: firstVar = CCCCC
secondSub: secondVar = DDDDD
firstSub: firstVar = CCCCC
firstSub: secondVar = BBBBB
Use of uninitialized value at test.pl line 3.
done: firstVar =
done: secondVar = DDDDD
The output from this example shows that
secondSub() could not access the $secondVar variable which was
created with my() inside firstSub(). Perl even prints out an
error message that warns about the uninitialized value. The $firstVar
variable, however, can be accessed and valued by secondSub().
Tip |
It's generally a better idea to use my()
instead of local() so that you can tightly control the scope of
local variables. Think about this way - It's four in the morning and the
project is due. Is that the time to be checking variable scope? No, using
my()enforces good programming practices and reduces
headaches. |
Actually, the my() function is even more complex than I've said. The easy definition is that it creates variables that only the current function can see. The true definition is that it creates variables with lexical scope. That is, variables that are local to the file, block, or eval. This distinction is only important when creating modules or objects so let's ignore the complicated definition for now. You'll hear more about it in Chapter 15, "Perl Modules."
If you remember, I mentioned calling parameters by reference. Passing parameters by reference means that functions can change the variable's value and the main program sees the change. When local() is used in conjunction with assigning the @_ array elements to scalars, then the parameters are essentially being called by value. The function can change the value of the variable, but only the function is affected. The rest of the program sees the old value.
Pseudocode |
Call the firstSub() function with two parameters: a list and a scalar. Define the firstSub() function. Assign the elements of the @_ array to @array and $firstVar. Print @array and $firstVar. |
firstSub((0..10), "AAAA"); sub firstSub{ local(@array, $firstVar) = @_; print("firstSub: array = @array\n"); print("firstSub: firstVar = $firstVar\n"); }
This program prints: firstSub: array = 0 1 2 3 4 5 6 7 8 9 10 AAAA
Use of uninitialized value at test.pl line 8.
firstSub: firstVar =
When the local variables are initialized, the
@array variables grab all of the elements in the @_ array,
leaving none for the scalar variable. This results in the uninitialized value
message displayed in the output. You can fix this by merely reversing the order
of parameters. If the scalar value comes first, then the function processes the
parameters without a problem.
Pseudocode |
Call the firstSub() function with two parameters: a scalar and a list. Define the firstSub() function. Assign the elements of the @_ array to $firstVar and @array. Print @array and $firstVar. |
firstSub("AAAA", (0..10)); sub firstSub{ local($firstVar, @array) = @_; print("firstSub: array = @array\n"); print("firstSub: firstVar = $firstVar\n"); }This program prints:
firstSub: array = 0 1 2 3 4 5 6 7 8 9 10 firstSub: firstVar = AAAA
Note |
You can pass as many scalar values as you want to a
function, but only one array. If you try to pass more than one array, the
array elements become joined together and passed as one array to the
function. Your function won't be able to tell when one array starts and
another ends. |
Pseudocode |
Call the firstSub() function. Define the firstSub() function. Print $count Increment $count by one. Call the firstSub() function recursively. |
firstSub(); sub firstSub{ print("$count\n"); $count++; firstSub(); }My system counts up to 127 before displaying the following message:
Error: Runtime exceptionWhile it is important to realize that there is a limit to the number of times your program can nest functions, you should never run into this limitation unless you are working with recursive mathematical functions.
Pseudocode |
Assign the return value from performCalc() to $temp. Print $temp. Define the performCalc() function which accepts two parameters and returns the sum of the parameters squared. An anonymous function is used to perform the square function. Assign my scalar variables values from the @_ parameter array. Define the private function referred to by $square. Return the first element of the @_ parameter array raised to the 2nd power. Return the value of $firstVar raised to the 2nd power and $secondVar raised to the 2nd power. |
$temp = performCalc(10, 10); print("temp = $temp\n"); sub performCalc { my ($firstVar, $secondVar) = @_; my $square = sub { return($_[0] ** 2); }; return(&$square($firstVar) + &$square($secondVar)); };This program prints:
temp = 200This example is rather trivial but it serves to show that in Perl it pays to create little helper routines. A fine line needs to be draw between what should be included as a private function and what shouldn't. I'd draw the line at 5 or 6 lines of code. Anything longer should probably be made into its own function. I'd also say that a private function should only have one purpose for existence. Performing a calculation and then opening a file is too much functionality for a single private function to have.
The rest of the chapter is devoted to showing you some of the built-in function of Perl. These little nuggets of functionality will become part of your arsenal of programming weapons.
Function | Description | ||
---|---|---|---|
chomp(STRING) OR chomp(ARRAY) | Uses the value of the $/ special variable to remove endings from STRING or each element of ARRAY. The line ending is only removed if it matches the current value of $/. | ||
chop(STRING) OR chop(ARRAY) | Removes the last character from a string or the last character from every element in an array. The last character chopped is returned. | ||
chr(NUMBER) | Returns the character represented by NUMBER in the ASCII table. For instance, chr(65) returns the letter A. | ||
crypt(STRING1, STRING2) | Encrypts STRING1. Unfortunately, Perl does not provide a decrypt function. This function is not supported under the Windows operating systems. | ||
index(STRING, SUBSTRING, POSITION) | Returns the position of the first occurrence of SUBSTRING in STRING at or after POSITION. If you don't specify POSITION, the search starts at the beginning of STRING. | ||
join(STRING, ARRAY) | Returns a string that consists of all of the elements of ARRAY joined together by STRING. For instance, join(">>", ("AA", "BB", "CC")) returns "AA>>BB>>CC". | ||
lc(STRING) | Returns a string with every letter of STRING in lowercase. For instance, lc("ABCD") returns "abcd". | ||
lcfirst(STRING) | Returns a string with the first letter of STRING in lowercase. For instance, lcfirst("ABCD") returns "aBCD". | ||
length(STRING) | Returns the length of STRING. | ||
ord(STRING) | Returns the ascii value of the first character of
STRING. If STRING is omitted, then $_ is used. For
instance, ord("ABC") returns the number 65.
| ||
rindex(STRING, SUBSTRING, POSITION) | Returns the position of the last occurrence of SUBSTRING in STRING at or before POSITION. If you don't specify POSITION, the search starts at the end of STRING. | ||
split(PATTERN, STRING, LIMIT) | Breaks up a STRING using PATTERN as the delimiter. The LIMIT parameter indicates how many parts to create from STRING. In an array context, it returns a list of the parts that were found. In a scalar context, the number of parts found. | ||
substr(STRING, OFFSET, LENGTH) | Returns a portion of STRING as determined by the OFFSET and LENGTH parameters. If LENGTH is not specified, then everything from OFFSET to the end of STRING is returned. A negative OFFSET can be used to start from the right side of STRING. | ||
uc(STRING) | Returns a string with every letter of STRING in uppercase. For instance, uc("abcd") returns "ABCD". | ||
ucfirst(STRING) | Returns a string with the first letter of STRING in uppercase. For instance, ucfirst("abcd") returns "Abcd". |
Note |
As a general rule, if Perl sees a number where it
expects a string, the number is quietly converted to a string without your
needing to do anything.
Some of these functions use the special variable $_ as the
default string to work with. More information about $ can be
found in Chapter 9, "Using
Files," and Chapter 12, "Using Special Variables."
|
The next few sections demonstrate some of these functions. After seeing some of them work, you'll be able to use the rest of them.
Pseudocode |
Assign $firstVar the return value from substr(). Print $firstVar. |
$firstVar = substr("0123BBB789", 4, 3); print("firstVar = $firstVar\n");This program prints:
firstVar = BBBThe substr() function starts at the fifth position and returns the next three characters. The returned string can be used for assignment like the above example, as an array element, for string concatention or any of a hundred other options.
Things become more interesting when you put the substr() function on the left-hand side of the assignment statement. Then, you can actually assign a value to the string that substr() returns.
Pseudocode |
Initialize $firstVar with a string literal. Replace the string returned by the substr() function with "AAA". Print $firstVar. |
$firstVar = "0123BBB789"; substr($firstVar, 4, 3) = "AAA"; print("firstVar = $firstVar\n");This program prints:
firstVar = 0123AAA789
Note |
The path name string has double-backslashes to
indicate to Perl that we really want a backslash in the string and not
some other escape sequence. You can read more about escape sequences in
Chapter 2, "Numeric and
String Literals." |
Pseudocode |
Assign a string literal to $pathName. Find the location of the last backslash by starting at the end of the string and working backward using the rindex() function. When the position of the last backslash is found, add one to it so that $position points at the first character ("W") of the filename. Use the substr() function to extract the filename and assign it to $fileName. Print $fileName. |
$pathName = "C:\\WINDOWS\\TEMP\\WSREWE.DAT"; $position = rindex($pathName, "\\") + 1; $fileName = substr($pathName, $position); print("$fileName\n");This program prints:
WSREWE.DATIf the third parameter - the length - is not supplied to substr(), it simply returns the sub-string that starts at the position specified by the second parameter and continues until the end of the string specified by the first parameter.
Function | Description |
---|---|
defined(VARIABLE) | returns true if VARIABLE has a real value and false if the variable has not yet been assigned a value. This is not limited to arrays, any data type can be checked. Also see the exists function for information about associative array keys. |
delete(KEY) | Removes the key-value pair from the given associative array. If you delete a value from the %ENV array the environment of the current process is changed, not that of the parent. |
each(ASSOC_ARRAY) | Returns a two-element list that contains a key and value pair from the given associative array. The function is mainly used so that you can iterate over the associate array elements. A null list is returned when the last element has been read. |
exists(KEY) | Returns true if the KEY is part of the specified associative array. For instance, exists($array{"Orange"}) returns true if the %array associative array has a key with the value of "Orange." |
join(STRING, ARRAY) | Returns a string that consists of all of the elements of ARRAY joined together by STRING. For instance, join(">>", ("AA", "BB", "CC")) returns "AA>>BB>>CC". |
keys(ASSOC_ARRAY) | Returns a list that holds all of the keys in a given associative array. The list is not in any particular order. |
map(EXPRESSION, ARRAY) | Evaluates EXPRESSION for every element of ARRAY. The special variable $_ is assigned each element of ARRAY immediately before EXPRESSION is evaluated. |
pack(STRING, ARRAY) | Creates a binary structure, using STRING as a guide, of the elements of ARRAY. You can look in Chapter 8, "References," for more information. |
pop(ARRAY) | Returns the last value of an array. It also reduces the size of the array by one. |
push(ARRAY1, ARRAY2) | Appends the contents of ARRAY2 to ARRAY1. This increases the size of ARRAY1 as needed. |
reverse(ARRAY) | Reverses the elements of a given array when used in an array context. When used in a scalar context, the array is converted to a string, and the string is reversed. |
scalar(ARRAY) | Evaluates the array in a scalar context and returns the number of elements in the array. |
shift(ARRAY) | Returns the first value of an array. It also reduces the size of the array by one. |
sort(ARRAY) | Returns a list containing the elements of ARRAY in sorted order. See Chapter 8, "References," for more information. |
splice(ARRAY1, OFFSET, LENGTH, ARRAY2) | Replaces elements of ARRAY1 with elements in ARRAY2. It returns a list holding any elements that were removed. Remember that the $[ variable may change the base array subscript when determining the OFFSET value. |
split(PATTERN, STRING, LIMIT) | Breaks up a string based on some delimiter. In an array context, it returns a list of the things that were found. In a scalar context, it returns the number of things found. |
undef(VARIABLE) | Slways returns the undefined value. In addition, it undefines VARIABLE which must be a scalar, an entire array or a subroutine name. |
unpack(STRING, ARRAY) | Does the opposite of pack(). |
unshift(ARRAY1, ARRAY2) | Adds the elements of ARRAY2 to the front of ARRAY1. Note that the added elements retain their original order. The size of the new ARRAY1 is returned. |
values(ASSOC_ARRAY) | Returns a list that holds all of the values in a given associative array. The list is not in any particular order. |
As I did with the string functions, only a few of these functions will be explored. Once you see the examples, you'll be able to handle the rest with no trouble.
The each() function does not loop by itself. It needs a little help from some Perl control statements. For this example, we'll use the while loop to print an associative array. The while (CONDITION) {} control statement continues to execute any program code surrounded by the curly braces until the CONDITION turns false.
Pseudocode |
Create an associative with number, color pairs. Using a while loop, iterate over the array elements. Print the key, value pair. |
%array = ( "100", "Green", "200", "Orange"); while (($key, $value) = each(%array)) { print("$key = $value\n"); }This program prints:
100 = Green 200 = OrangeThe each() function returns false when the end of the array is reached. Therefore, you can use it as the basis of the while's condition. When the end of the array is reached, the program continues execution after the closing curly brace. In this example, the program simply ends.
Since we haven't talked about disk files yet, we'll need to emulate a disk file with an associative array. And instead of using customer's address, we'll use customer number, customer name pairs. First, we see what happens when an associative array is created and two values have the same keys.
Pseudocode |
Call the createPair() function three times to create three key, value pairs in the %array associative array. Loop through %array, printing each key, value pair. Define the createPair() function. Create local variables to hold the key, value pair passed as parameters. Create an array element to hold the key, value pair. |
createPair("100", "Kathy Jones"); createPair("200", "Grace Kelly"); createPair("100", "George Orwell"); while (($key, $value) = each %array) { print("$key, $value\n"); }; sub createPair{ my($key, $value) = @_; $array{$key} = $value; };This program prints
100, George Orwell 200, Grace KellyThis example takes advantages of the global nature of variables. Even though the %array element is set in the createPair() function, the array is still accessible by the main program. Notice that the first key, value pair (100 and Kathy Jones) are overwritten when the third key, value pair is encountered. You can see that it is a good idea to be able to determine when an associative array element is already defined so that duplicate entries can be handled. The next program does this.
Pseudocode |
Call the createPair() function three times to create three key, value pairs in the %array associative array. Loop through %array, printing each key, value pair. Define the createPair() function. Create local variables to hold the key, value pair passed as parameters. If the key, value pair already exists in %array then increment the customer number by one. Check to see if the new key, value pair exists. If so, keep incrementing until a non-existent key, value pair is found. Create an array element to hold the key, value pair. |
createPair("100", "Kathy Jones"); createPair("200", "Grace Kelly"); createPair("100", "George Orwell"); while (($key, $value) = each %array) { print("$key, $value\n"); }; sub createPair{ my($key, $value) = @_; while (defined($array{$key})) { $key++; } $array{$key} = $value; };This program prints:
100, Kathy Jones 101, George Orwell 200, Grace KellyYou can see the customer number for George Orwell has been changed to 101. If the array had already had an entry for 101, the George Orwell' new customer number would be 102.
You learned about the scope of variables and how all variables are global by default. Then, you saw how to create variable with local scope using local() and my(). My() is the better choice in almost all situations because it enforces local scope and limits side effects from function to inside the functions.
Then you saw that it was possible to nest function calls which means that one function can call another which in turn can call another. You might also call this a chain of function calls. Private functions were introduced next. A private function is one that can only be used inside the function that defines it.
A list of string functions was then presented. These included functions to remove the last character, encrypt a string, find a sub-string, convert array elements into a string, change the case of a string characters, and find the length of a string. Examples were shown about how to change a string's characters and how to search a string.
The section on array functions showed that Perl has a large number of functions that deal specifically with arrays. The list of functions included the ability to delete elements, return key and value pairs from associative arrays, reverse an array's elements, and sort an array. Examples were shown for printing an associative array and checking for the existence of an element.
The next chapter, "Statements," goes into detail about what statements are and how you create them. The information that you learned about variables and functions will come into play. You'll see how to link variables and functions together to form expressions and statements.
firstSub(); sub firstSub{ print("$count\n"); $count++; firstSub(); }