Chapter 15 – Perl Subroutines

functions. We learned about some built-in Perl functions in various earlier chapters such as chomp() and reverse(@array). User-created functions gives us more control and can be used to clean up your code.

Like variables, sub routines have their own namespace and are generally preceded by it’s defining character; the ampersand &. Though the ampersand preceding your function (we will use function and sub routine interchangeable from here on out) isn’t always required, get accustomed to using it unless you are absolutely sure it’s optional.

Depending on whom you talk to and what programming background they have, they will tell you different places to create your sub routines. A few programmers will tell you it’s best to create your sub routines at the top of your script, others will say it’s better format at the bottom. To each their own but on a personal note, the author of this article prefers to create sub routines at the bottom of the script as it helps remove the clutter from the main script.

To call a sub routine:

&compare;

The name compare in our example isn’t special. It can contain any letters, numbers or underscores as long as it does not begin with a digit. The following two examples are all acceptable function calls and names.

&money;
&magick;


Creating a sub routine

Creating your sub routine isn’t much different than creating a conditional or loop in terms of syntax.

sub compare
{
  # do this
}

The main difference between a simple eq or == and a m//, is one tests for equality and the other tests for the existence of the value inside the string.

When creating your sub routine you don’t use the sub character &, you precede it with sub. Within the user-created function you can accomplish any tasks your script requires such as math, string manipulation, page parsing, etc. Functions are global by nature but the variables used within your functions may either be locally scoped or global.


Locally scoped variables VS Global variables

Before we continue it is important to recognize the differences between locally scoped “private” variables and global variables which we’ve been using through our sample codes in the past tutorials.

A global variable is any variable that is accessible to any loop, sub routine or function. This is to say, a global variable can be altered anywhere within your script. Under the use strict pragma, we are required to my our variable declarations.

When you my a variable within a function call or a loop (any time you’re within { and } braces) the variable becomes locally scoped or “private” and can only be used within the braces you created the variable inside.

If you don’t require the variable to be used elsewhere, you will save memory if you declare them locally. Locally scoped variables have quicker access times.

To make a variable global, you declare the variable within the main script’s scope where it’s not interlocked within { } braces.

The following example demonstrates locally scoped variables because the variables are declared within the function. Since my is used within the braces, the $num1 and $num2 variables will not be accessible to any other function or to the main script itself.

sub compare
{
  my $num1 = 1;
  my $num2 = 2;

  print $num1 + $num2;
}

The next example demonstrates global variables. The variables are declared with my inside the main scope of the script; not within the sub routine. The variable assignments inside the sub routine will change the variable contents when you use it elsewhere.

my $num1;
my $num2;
sub compare
{
  $num1 = 1;
  $num2 = 2;

  print $num1 + $num2;
}


Returned Values

Sometimes you will want to return a locally scoped variable within your function to be used within your main script (or an entirely separate function). The last calculation in the function will be the returned value.

In the example below, the last known calculation was the addition of our two locally scoped variables. Even though they are locally scoped you can retrieve the return value and store it into a variable by assigning the variable to the sub routine itself. my $count = &add;

my $cnt = &add;
print $cnt;

sub add
{
  my $num1 = 1;
  my $num2 = 2;
  $num1 + $num2;
}

The last calculation doesn’t have to be math-related; any calculation on the final line will be the returned value.

Every function has a return value, whether that value means anything important is up to you. The value is either 1 if the function ends successfully, 0 if it doesn’t OR any value you tell it to return.

For example, if you end the function with a print statement the return value probably won’t be what you expect.

my $cnt = &add;
print $cnt;

sub add
{
  my $num1 = 1;
  my $num2 = 2;
  my $count = $num1 + $num2;
  print “$count”;
}

31

The subroutine above has a return value of 31, not the value of 3 that you’d expect from the calculation. This is because you are getting two separate return values. The first number (3) is the correct calculation of 1+2. The (1) at the end is returned because the final calculation was a print statement and it was successful. Since it was successful, the second returned value is a 1.

To make the return value accurate, the final calculation needs to be a variable value.


The Return operator

There may be times the value calculation within a function may be required before the entire sub routine is executed. To return values when you need them, the return operator sends the values back to your script.

my $cnt = &add;
print $cnt;

sub add
{
  my $num1 = 1;
  my $num2 = 2;
  return $num2;
  my $count = $num1 + $num2;
  print “The count was $count”;
}

2

Using the return operator will end the function prematurely. This is to say, anything after the return will not be executed. In the example above, neither the math addition nor the print out was processed.


Arguments

There will be times you don’t want to manually create variables within your function. When calling your sub routine, arguments can be passed directly to it for processing.

&add(1,2);

Arguments are passed within the optional parenthesis when you call your function. If you have more than one argument, as seen in the example above, the arguments are separated by commas.

Perl holds no real limitations in terms of the number of passable arguments as long as your system has the resources to accommodate.

The values passed are stored within a special Perl variable @_. Don’t get this confused with the special variable $_ when you use this. Remember, arrays have [] brackets at the end for indexes while the scalar variables do not.

my $cnt = &add(1,2);
print $cnt;

sub add
{
  my ($num1, $num2) = @_;
  return $num2;
}

2

The example above is passing 2 arguments to the function; 1 and 2. Inside the sub routine we are creating our local private variables for both of our arguments and are assigning them to @_. @_ is the special variable we just talked about that stores all of the passed arguments as array indexes. The first argument is @_[0], the second argument would be @_[1] (of course you’d write these as $_[0] and $_[1]) but when you assign to the array @_ itself, the variables pull the the indexes in order they are created.

To demonstrate single assignments, we would use the following.

my $cnt = &add(1,2);
print $cnt;

sub add
{
  my $num1 = $_[0];
  my $num2 = $_[1];

  $num1 + $num2;
}

3

This is a clear example of how the arguments passed are literally array indexes. Again, don’t confuse $_[0] and $_[1] as the special variable $_. The arguments are passed as array elements of @_.

What would happen if you pass more arguments to the sub routine than you expected? Luckily, if you don’t assign a variable to the extra argument(s), they’ll be ignored. In the previous example, if you passed my $cnt = &add(1,2,3); the results would be identical as the extra argument is being ignored since it wasn’t called or stored.

Sometimes this is good enough. The extra arguments are ignored but the circumstance may arise where you need to know for certain how many arguments was passed.

Below is one way to check how many arguments were really passed. If you assign a scalar to an array (even a special array), the scalar variable will be set to the number of elements inside the array. From here, you can build your own if/else conditionals based on your immediate requirements.

my $cnt = &add(12,34,56,78);
print $cnt;

sub add
{
  my $num_of_arguments = @_;
  if ($num_of_arguments != 2)
  {
    print “Incorrect number of arguments passed”;
   }
 {
else
{
  my ($num1, $num2) = @_;
  $num1 + $num2;
}
}

46

If the script requires more explicit argument checking, you may also check to see if an argument is within the appropriate range or apply a regex to see if it contains a specific search phrase. The script can be as specific as it needs to be to prevent errors from occurring.

my $cnt = &add(12,34,56,78);
print $cnt;

sub add
{
  my $data = $_[0];
  if ($data =~ m/[^a-zA-Z]/)
  {
    print “The arguement was not correct\n”;
  }
  else
  {
    return $data;
  }
}

46

In the beginning, it was stated that sub routines assist the code maintainers by cleaning out our code. It’s fine and dandy if you take our word for it but let’s see a real example of how sub routines can actually help make your code cleaner and more maintainable.

The following script is no other than the Word Scramble game we have for downloads here on SpyderScripts.com. The sub routines which contain most of the processing and “ugly” code are at the bottom of the script. All that is on the top is our basic framework of a layout with calls to our sub routine.

Since we can see our entire framework, if something goes wrong we know exactly which sub routine to look through.

001: #!/usr/bin/perl
002:
003: use warnings;
004:
005: use strict;
006:
007: #####################
008: # Configuration section
009: #####################
010:
011: my $beep_on_right = “1″;
012: ## If set, a beeping sound will play when you guess the word right
013:
014: my $max_attempts = 5;
015: ## The number of guesses you want before it moves on to the next word
016:
017: my $dictionary = “dict.txt”;
018:
019: my $hints = “2″;
020: ## The number of hints per word allowed
021:
022: #####################
023: # Do not edit below this line
024: #####################
025:
026: ############
027: # In-game variables
028: ############
029: my ($guess, $word, $backtotop);
030: my $hint_word = 0;
031: my $hint_count = 0;
032: my $won_game = 0;
033: my $lost_game = 0;
034: my $total_hints = 0;
035:
036:
037: &greetings;
038:
039: while (1)
040: {
041:
042: &pick_word;
043:
044: my $attempts = 0;
045:
046: while ($attempts++ < $max_attempts)
047: {
048: &guess;
049: &check_guess($backtotop);
050:
051: if ($backtotop)
052: {
053: last;
054: }
055: }
056:
057: while ($attempts > $max_attempts)
058: {
059: &word_solution;
060: last;
061: }
062:
063: }
064:
065: ##################################################
066: # Prepare subroutines beginning here
067: ##################################################
068:
069:
070: ###########
071: # Welcome message
072: ###########
073: sub greetings
074: {
075: system cls;
076: print <<” END”;
077: #####################################
078: ## WORD SCRAMBLE ##
079: #####################################
080: # #
081: # #
082: # Directions: Try to guess what the #
083: # scrambled word is before your #
084: # guesses run out. #
085: # #
086: # You may type in “exitgame” at any #
087: # time to quit WORD SCRAMBLE. #
088: # #
089: # You are allowed a few hints. Type #
090: # *hint* as your guess! #
091: #####################################
092: END
093:
094: }
095:
096: ###########
097: # Pick a random word
098: ###########
099: sub pick_word
100: {
101: open (DICT, $dictionary) or die “Cannot open $dictionary because: $!”;
102: my @words = <DICT>;
103:
104: foreach (@words)
105: {
106: chomp($_);
107: }
108:
109: $word = $words[rand @words];
110: $word = lc $word;
111:
112: my @scramble = split(//, $word);
113:
114: @scramble = sort { (-1,1)[rand 2] } @scramble;
115:
116: print “\n\nGuess the scrambled word: “;
117: foreach (@scramble) {print $_};
118: print “\n\n”;
119: }
120:
121:
122: ###########
123: # Take their input for guess
124: ###########
125: sub guess
126: {
127: print “\tGuess:”;
128: $guess = <STDIN>;
129: $guess = lc $guess;
130: chomp($guess);
131: }
132:
133:
134: ###########
135: # Answer checking and to see if they quit the game
136: ###########
137: sub check_guess
138: {
139: if ($guess eq “exitgame”)
140: {
141: print <<” END”;
142: \n\n
143: ##################################################
144: # SESSION STATS
145: ##################################################
146: #
147: # Words guessed: $won_game
148: # Words wrong: $lost_game
149: # Total hints: $total_hints
150: #
151: ##################################################
152: END
153:
154: print “\n\nWORD SCRAMBLE will now close.\n\n”;
155: exit;
156: }
157: elsif ($guess eq “*hint*”)
158: {
159: &display_hint;
160: }
161: elsif ($guess ne “$word”)
162: {
163: print “\t$guess is incorrect.\n”;
164: }
165: else
166: {
167: $won_game++;
168: print “\n##################################################\n”;
169: print “#Congratulations! The scrambled word was $word.\n”;
170: print “##################################################\n\n”;
171:
172: if ($beep_on_right) { print “\a”; }
173: $backtotop = “1″;
174: }
175: }
176:
177: ###########
178: # Give word solution
179: ###########
180: sub word_solution
181: {
182: $lost_game++;
183: print “\n:( :( :( :( :( :( :( :( :( :( :( :( :( \n”;
184: print “The correct answer was: $word\n”;
185: print “:( :( :( :( :( :( :( :( :( :( :( :( :( \n\n”;
186: }
187:
188: ###########
189: # Give word solution
190: ###########
191: sub display_hint
192: {
193: if ($hint_count >= $hints)
194: {
195: print “You are out of hints.\n\n”;
196: }
197: else
198: {
199: $hint_count++;
200: $total_hints++;
201: $hint_word = substr $word, 0, $hint_count;
202:
203: print “HINT: $hint_word\n\n”;
204: }
205: }

Challenges

1) What is another name for a sub routine?
————————————————————————
A function
————————————————————————

2) What is the special variable that sub routine arguments are stored in?
————————————————————————
They are stored in the @_ array variable
————————————————————————

3) What is the easiest way to count how many elements are found within an array?
————————————————————————
my $count = @array;

When you assign a scalar variable to an array, the results stored is the number of elements found within the array itself.
———————————————————————–

VN:F [1.9.22_1171]
Rating: 0.0/10 (0 votes cast)
VN:F [1.9.22_1171]
Rating: 0 (from 0 votes)