Skip to content

Perl Subroutines

In Perl, a subroutine (often called a function) is a block of code designed to perform a specific task. By dividing your code into subroutines, you can make your programs more modular, reusable, and easier to understand. Each subroutine ideally performs a single, well-defined operation.

While the terms subroutine and function are used interchangeably in Perl, a ‘method’ typically refers to a subroutine associated with an object or a class in object-oriented programming.

The general form of a subroutine definition in Perl is:

sub subroutine_name {
# Body of the subroutine
my @args = @_;
# ... code ...
return $result; # Optional
}

To call a subroutine, you typically use its name followed by parentheses containing any arguments:

subroutine_name(argument1, argument2);

Older Perl code (prior to Perl 5) might use an ampersand & prefix to call subroutines (e.g., &subroutine_name;). While this syntax may still work, it’s not recommended for modern Perl as it can bypass subroutine prototypes and is generally considered outdated.

Perl compiles your entire program before execution, so you can define your subroutines anywhere in your script (e.g., at the end of the file). However, for readability, it’s common to group them or place them before their first use.

Here’s a simple example:

#!/usr/bin/perl
use strict;
use warnings;
# Subroutine definition
sub greet {
print "Hello, Modern Perl!\n";
}
# Subroutine call
greet();

Executing this program produces:

Hello, Modern Perl!

Arguments passed to a subroutine are available within the subroutine via a special array named @_. The first argument is $_[0], the second is $_[1], and so on. It’s good practice to copy @_ to named lexical variables at the beginning of your subroutine for clarity and safety (as @_ elements are aliases to the original arguments).

#!/usr/bin/perl
use strict;
use warnings;
sub display_info {
my ($name, $age) = @_;
print "Name: $name, Age: $age\n";
}
display_info("Alice", 30);

Output:

Name: Alice, Age: 30

When passing multiple arrays or hashes, they are flattened into a single list in @_. To pass them as distinct entities, you should use references. This topic is typically covered in detail when discussing Perl references.

Let’s look at an example that calculates the average of a list of numbers:

#!/usr/bin/perl
use strict;
use warnings;
sub calculate_average {
my @numbers = @_;
return unless @numbers; # Handle empty list
my $sum = 0;
foreach my $num (@numbers) {
$sum += $num;
}
return $sum / @numbers;
}
my $avg = calculate_average(10, 20, 30, 40);
print "Average: $avg\n"; # Output: Average: 25
my $avg_empty = calculate_average();
if (defined $avg_empty) {
print "Average (empty): $avg_empty\n";
} else {
print "Cannot calculate average for an empty list.\n";
}

Output:

Average: 25
Cannot calculate average for an empty list.

Since @_ is an array, it naturally handles lists. If you pass a mix of scalars and an array, the array’s elements will be part of the flattened list in @_. It’s often best to pass an array as the last argument if you intend to capture its elements easily, or use references.

#!/usr/bin/perl
use strict;
use warnings;
sub print_items {
my $prefix = shift @_; # First argument
my @items = @_; # Rest of the arguments
print "$prefix: ", join(", ", @items), "\n";
}
my @fruits = ("Apple", "Banana", "Cherry");
print_items("Fruits", @fruits);
# Output: Fruits: Apple, Banana, Cherry
print_items("Numbers", 1, 2, 3);
# Output: Numbers: 1, 2, 3

When a hash is passed to a subroutine, it’s typically flattened into a list of key-value pairs in @_. You can reconstruct the hash inside the subroutine.

#!/usr/bin/perl
use strict;
use warnings;
sub print_hash_data {
my %data = @_;
foreach my $key (sort keys %data) {
print "$key: $data{$key}\n";
}
}
my %user_info = (
name => "Bob",
id => 101,
city => "New York"
);
print_hash_data(%user_info);

Output (order of keys might vary without sort):

city: New York
id: 101
name: Bob

Subroutines can return values using the return keyword. If return is omitted, the value of the last evaluated expression in the subroutine is automatically returned. You can return scalars, lists, or references.

To return multiple arrays or hashes distinctly, you must return references to them.

Example (revisiting calculate_average):

#!/usr/bin/perl
use strict;
use warnings;
sub calculate_sum_and_average {
my @numbers = @_;
return (undef, undef) unless @numbers;
my $sum = 0;
foreach my $num (@numbers) {
$sum += $num;
}
my $average = $sum / @numbers;
return ($sum, $average); # Return a list
}
my ($total, $avg_val) = calculate_sum_and_average(10, 20, 30);
if (defined $total) {
print "Sum: $total, Average: $avg_val\n";
} else {
print "Could not calculate.\n";
}
# Output: Sum: 60, Average: 20

Private Variables in a Subroutine (Lexical Scope)

Section titled “Private Variables in a Subroutine (Lexical Scope)”

By default, variables declared without my (e.g., $foo = 1;) have package (global) scope if use strict; is not in effect. However, it is a strong best practice to always use strict; and use warnings;. This requires you to declare variables with my, our, or state.

The my keyword declares lexical variables. These variables are private to the enclosing block (e.g., a subroutine, a loop, or any pair of curly braces {}). They are not visible outside their scope.

sub example_scope {
my $lexical_var = "I'm local to this sub";
# $lexical_var is only accessible here
my ($another_var, @an_array, %a_hash); # Declaring multiple lexicals
}

Example distinguishing global-like (package) and lexical variables:

#!/usr/bin/perl
use strict;
use warnings;
our $package_string = "Hello, Package!"; # Declared with 'our' for package scope
sub print_scoped_hello {
my $lexical_string = "Hello, Lexical!";
# $package_string is accessible here
print "Inside subroutine (lexical): $lexical_string\n";
print "Inside subroutine (package): $package_string\n";
$package_string = "Changed by subroutine"; # Modifies package variable
}
print_scoped_hello();
print "Outside subroutine (package): $package_string\n";
# print $lexical_string; # This would be a compile-time error

Output:

Inside subroutine (lexical): Hello, Lexical!
Inside subroutine (package): Hello, Package!
Outside subroutine (package): Changed by subroutine

local() gives a temporary value to a package (global) variable, including special variables like $_ or $/. The original value is restored when the current block is exited. This is known as dynamic scoping. local() is rarely needed in modern Perl compared to my (lexical scoping), which is generally preferred for user-defined variables due to its more predictable behavior.

Use local() with caution, primarily for localizing changes to Perl’s special global variables or existing package variables where lexical scoping isn’t an option or desired.

#!/usr/bin/perl
use strict;
use warnings;
our $global_var = "Initial Global Value";
sub show_local_effect {
print "Inside show_local_effect (before local): $global_var\n";
local $global_var = "Temporary Localized Value";
print "Inside show_local_effect (after local): $global_var\n";
call_another_sub();
print "Inside show_local_effect (after call_another_sub): $global_var\n";
}
sub call_another_sub {
print "Inside call_another_sub: $global_var\n"; # Sees the localized value
}
print "Outside (before call): $global_var\n";
show_local_effect();
print "Outside (after call): $global_var\n";

Output:

Outside (before call): Initial Global Value
Inside show_local_effect (before local): Initial Global Value
Inside show_local_effect (after local): Temporary Localized Value
Inside call_another_sub: Temporary Localized Value
Inside show_local_effect (after call_another_sub): Temporary Localized Value
Outside (after call): Initial Global Value

state variables (available since Perl 5.9.4, commonly enabled with use v5.10; or use feature 'state';) are lexically scoped variables that persist their values between calls to the subroutine. They are initialized only once, the first time the subroutine is called.

#!/usr/bin/perl
use strict;
use warnings;
use feature 'state'; # or use v5.10; or higher
sub persistent_counter {
state $count = 0; # Initialized only once
$count++;
print "Counter is now: $count\n";
}
persistent_counter(); # Output: Counter is now: 1
persistent_counter(); # Output: Counter is now: 2
persistent_counter(); # Output: Counter is now: 3

This is a cleaner way to achieve what previously required closures or package variables.

The context in which a subroutine is called determines what kind of value it is expected to return. Perl subroutines can behave differently in scalar context (expecting a single value) versus list context (expecting a list of values). The wantarray function can be used inside a subroutine to check its call context.

For example, the built-in localtime() function:

#!/usr/bin/perl
use strict;
use warnings;
# Scalar context: expects a single string value
my $datestring = localtime(time);
print "Scalar context: $datestring\n";
# List context: expects a list of time components
my @time_parts = localtime(time);
# ($sec, $min, $hour, $mday, $mon, $year_offset, $wday, $yday, $isdst) = localtime(time);
# $mon is 0-11, $year_offset is years since 1900
print "List context (hour): $time_parts[2]\n";
sub contextual_return {
if (wantarray) { # list context
return ("alpha", "beta", "gamma");
} else { # scalar context
return "delta";
}
}
my @list_result = contextual_return();
print "List result: @list_result\n"; # Output: List result: alpha beta gamma
my $scalar_result = contextual_return();
print "Scalar result: $scalar_result\n"; # Output: Scalar result: delta

Understanding context is crucial for writing flexible and correct Perl subroutines, especially when interacting with built-in functions or designing complex APIs.