- Perl ›
- Subroutine ›
- here
Subroutine reference
A subroutine reference is a reference to a subroutine.
# Create reference my $twice_ref = \&twice; # Dereference and call my $twice_num = $twice_ref->($num); sub twice { return $_[0] * 2; }
Express the subroutine itself with & twice. (Similar to an array with @and a hash with%, a subroutine is represented with &.) Normally, subroutine are called like twice ($num). You can also call it explicitly, like & twice ($num).
To represent a reference to a subroutine, use \&twice. To call the subroutine, dereference the reference to the subroutine, as in $twice_ref->(1). A description very similar to a hash or array dereference.
See the following articles for a detailed explanation of the reference.
Reference to anonymous subroutine
You can create a reference to an unnamed subroutine such as sub {}. Write as my $sum_ref = sub {} ;. The subroutine itself is not executed, but a reference to the subroutine is assigned to $sum_ref.
my $sum_ref = sub { my @nums = @_; my $total; for my $num (@nums) { $total += $num; } return $total; };
You can create a reference to a subroutine and pass it as an argument to the subroutine. In Perl, you can use a subroutine reference to pass a subroutine to a subroutine.
You can also use a reference to a subroutine to execute the subroutine selectively. I will see a concrete example next time.
Polymorphism using reference to subroutine
This is an example that realizes polymorphism using a reference to a subroutine.
use strict; use warnings; my @nums = (1, 2, 3); print "1: Calculate the sum and average in order using the reference to the subroutine.\n"; # Create a array of reference to # subroutine. my @calc_funcs = (\&sum, \&average); for my $calc_func (@calc_funcs) { my $result = $calc_func->(@nums); print "Result: $result\n"; } print "\n"; sub sum { my @nums = @_; my $total; for my $num (@nums) { $total += $num; } return $total; } sub average { return sum (@_)/@_; } print "2: Selectively process using reference to subroutine.\n"; my $how_to_calc = 'sum'; my $calc_func; if ($how_to_calc eq 'sum') { $calc_func = \∑ } else { $calc_func = \&average; } my $result = $calc_func->(@nums); print "Calculation method: $how_to_calc\n". "Result: $result\n\n"; print "3: Selectively process using reference to subroutine and hash.\n"; $how_to_calc = 'average'; my %calc_table = (sum => \&sum, average => \&average); # $calc_table{'average'} is a reference to the subroutine # Since is included, dereference it with the->operator. # Call a subroutine. $result = $calc_table{$how_to_calc}->(@nums); print "Calculation method: $how_to_calc\n". "Result: $result\n\n";
Apply subroutine to variable in sequence
my @calc_funcs = (\&sum, \&average); for my $calc_func (@calc_funcs) { my $result = $calc_func->(@nums); }
To apply subroutine in sequence to variable, use a reference to the subroutine. Store the reference to the subroutine in an array and call the subroutine repeatedly in for loop.
Image of variable and subroutine
| - -- -- -- | | | <- -- --- Subroutine 1 | | | Variable | <- -- --- Subroutine 2 | | | | <- -- --- Subroutine 3 | - -- -- -- |
An image of applying subroutine in order to a certain type of argument. The argument is fixed, and the image of the subroutine changing.
Polymorphism using reference to subroutine
# How to branch with if statement my $how_to_calc = 'sum'; my $calc_func; if ($how_to_calc eq 'sum') { $calc_func = \∑ } else { $calc_func = \&average; } my $result = $calc_func->(@nums);
Polymorphism is a term that means "the same description but different functions are called". Object-oriented often means that "different methods are applied depending on the object".
In this example, the statement $calc_func->(@num) has different meanings depending on what is assigned to $calc_func. If \$sum is assigned, the sum subroutine is called, and if \$average is assigned, the average subroutine is called.
In java, overloading and overriding are the ways to achieve polymorphism, and in C they are function pointers.
Beautiful method using hash
$how_to_calc = 'average'; my %calc_table = (sum => \&sum, average => \&average); $result = $calc_table{$how_to_calc}->(@nums);
I think hash method is the most sophisticated form of function-referenced polymorphism.
Although it is a selective process, it does not use any if statement. The process is branched by calling the subroutine corresponding to the hash key.
Implementation of signal handler in subroutine
A signal handler is a subroutine that is executed triggered by a signal. A signal is a signal that the OS sends to a process. Ctrl + c asks the OS to "send an INT signal to the currently running process." The alerm function also asks the OS to "send a LARM signal to the currently running process after n seconds".
$SIG{INT} = sub { die "INT signal sent to this process."; }; $SIG{ALRM} = sub { die "ALRM signal was sent to this process."; };
To catch the INT signal, assign a reference to the subroutine to $SIG{INT}. This example defines an anonymous subroutine and assigns its reference.
There are a lot of things to note about signals, but I won't write them here. I write it when I write about interprocess communication.
A rudimentary implementation of event - driven programming
This is an example of a rudimentary implementation of event-driven programming.
use strict; use warnings; # Event handler my $text = "aIue !!! o"; # Pass the event handler as the argument. parse ( $ text, start_h => \&start, lc_h => \&found_lc, end_h => \&end, ); # Subroutine that parses a string (event detection function) sub parse { # Receives a hash of text and event handlers. my ($text,%handler) = @_; # First called handler $handler{start_h}->(); # Analyze one character at a time. for my $i (0 .. length($text) ―― 1) { my $char = substr($text, $i, 1); # Call an event handler when it finds a lowercase letter. # Made available in callback functions # Pass the character and position as arguments. if ($char =~ /[a-z]/) { $handler{lc_h}->($char, $i); } } # Last called handler $handler{end_h}->(); } # Event handler (callback function) sub start { print "String parsing has started.\n"; } sub found_lc { # Called when a lowercase letter is found. # You can receive characters and positions as arguments. my ($char, $pos) = @_; print "The position of $char is $pos.\n"; } sub end { print "The parse of the string is finished.\n"; }
(Reference) substr function
(1) Subroutine for detecting events
# Call a function to detect an event. parse ( $text, start_h => \&start, lc_h => \&found_lc, end_h => \&end, ); # Event detection function sub parse { # Receives a hash of text and event handlers. my ($text,%handler) = @_; # First called handler $handler{start_h}->(); # ... }
In event-driven programming, "event detection" and "event execution" are separated. To do this, first implement the process of detecting the event as a subroutine.
The argument uses a hash to pass a reference to the subroutine. This subroutine is executed when an event occurs.
(2) Subroutine (callback function) executed when an event occurs
sub start { print "String parsing has started.\n"; } sub found_lc { # Called when a lowercase letter is found. # You can receive characters and positions as arguments. my ($char, $pos) = @_; print "The position of $char is $pos.\n"; } sub end { print "The parse of the string is finished.\n"; }
Subroutines that are executed when an event occurs are commonly referred to as callback functions. Here, write the processing to be performed when an event occurs.
It is a function that detects an event, and if you pass an argument when calling the callback function, it can be received and used by the callback function.
Example code
Subroutine example code.
Subroutine reference
This is an example to create a reference to a subroutine.
use strict; use warnings; # Subroutine reference # Prefix\to the subroutine # It will be a reference. # & twice refers to the twice subroutine itself. print "1: Reference to subroutine\n"; my $twice_ref = \&twice; # When calling # $sub_ref->(arg1, arg2) # Call it like . # When no argument is needed # $sub_ref->(). my $num = 1; my $twice_num = $twice_ref->($num); print "Twice of $num is $twice_num.\n\n"; sub twice { return $_[0] * 2; } # Assign a reference to a subroutine that does not have the name sub {} into $sum_ref. print "2: Reference to anonymous subroutine\n"; my $sum_ref = sub { my @nums = @_; my $total; for my $num (@nums) { $total += $num; } return $total; }; my $total = $sum_ref->(1, 2, 3); print "The sum of 1,2,3 is $total.\n\n";
Signal handler
This is an example using a signal handler. I'm using while statement to do an infinite loop.
use strict; use warnings; print "1: Capture INT signal with signal handler.\n"; $SIG{INT} = sub { die "INT signal sent to this process."; }; # infinite loop while (1) {}; # Use Ctrl + c to send a INT signal to a running process.
use strict; use warnings; print "2: Capture ALRM signal with signal handler.\n"; $SIG{ALRM} = sub { die "ALRM signal was sent to this process."; }; # After 2 seconds, send an ALRM signal to the running process. alarm 2; # infinite loop while (1) {};