Perl Subroutines
Simple Declaring and Defining a Subroutine
# Declare and define test1
sub test1{print "this is test1";}
# Predeclared, so we can call it without parens
test1;
# test is NOT Predeclared, so we use parens
test2();
# Declare and Define test2
sub test2{print "this is test2";}
# Declare test1
sub test3;
# test3 is predeclared, so we can call it w/o parens
test3;
# Define test3
sub test3{print "this is test3";}
Calling Subroutines: With and W/o &
- With &:
- arguments must be in parens.
- no args and no parens, then @_ is passed
- No prototype checking???
- With no &:
- args require parens unless predeclared
Calling Subroutines: With and W/o ()
- With ():
- pre and post the same
- & optional
- args as expected
- with no ():
- & and no args sends @_
- & and args is error
- no & like parens, if predeclared
- no & allows no parens
Calling Subroutines: With and W/o pre and post declared
- These always work, whether mysub is pre or post declared:
mysub(arglist); # passes arglist as @_, arglist can be empty
&mysub(arglist); # passes arglist as @_, arglist can be empty
&mysub; # SPECIAL CASE: Passes current @_ to mysub
These only work if mysub is predeclared:
mysub arglist; # passes arglist as @_
mysub; # passes () in @
These is always an error, whether subroutine is pre or post declared:
&mysub arglist; # ERROR unless arglist happens to be in ()
& removes prototype checking
Passing Params: List @_
sub sumSquares{
print join ", ", @_, "\n";
$a = shift @_;
$b = shift @_;
return $a*$a + $b*$b;
}
print sumSquares 3, 4; # 25
Param Lists are Flattened
sub sumList{
print join ", ", @_, "\n";
foreach $i (@_){
$sum = shift @_;
}
return $sum;
}
print sumList 2, 3, 4, 5; # 14
@a = (1..5);
@b = (6..10);
print sumList @a; # 15
print sumList (@a, @b); # 55
Returning Multiple Values
sub inOrder{
if (@_[0] < @_[1]){
return $_[0], $_[1];
} else {
return $_[1], $_[0];
}
}
$a = 3; $b = 1;
($a, $b) = inOrder $a, $b;
print $a, $b
$a = 1; $b = 3;
($a, $b) = inOrder $a, $b;
print $a, $b
Last Value Calculated is Returned
sub sumSquares{
print join ", ", @_, "\n";
my $a = shift @_;
my $b = shift @_;
my $sum = $a*$a + $b*$b;
This return is redundant
# return $sum;
}
print sumSquares 3, 4; # 25
- But be careful of the last value calculated in a loop!
All Variables are Global Unless Declared as my Variables
sub foo{
my $localVar = 11;
$globalVar = 22;
}
foo;
print $localVar; # uninitialized
print $globalVar; # 22
- Be careful!
- keyword local is similar, but more complex.
A Larger Example
Use English
- When using English, params are in
@ARG
@ARG is a List of References to In Out Variables
- @ARG is a list of references to the actual scalar parameters
- As a result, changes to elements of @ARG are reflected
in the original list
- Thus parameters are effectively in out mode
use English;
sub changeParams{
$ARG[0] = 99;
$ARG[1]++;
}
@a = (1,2,3,4);
print @a; # 1234
changeParams @a;
print @a; # 99334
Attempting to modify read only variables will fail:
changeParams 1, @a;
Parameter Passing: Simulating In Mode Parameters
- Use locals to get the effect of in mode parameters
- Changes to elements of @ARG are not reflected in the original list
use English;
sub dontChangeParams{
($a, $b, $c, $d) = @arg;
$a = 99;
$b++; $c++; $d++;
}
@a = (1,2,3,4);
print @a; # 1234
dontChangeParams @a;
print @a; # 1234
Passing Two Lists, with References
- Remember lists are flattented:
sub printIt{
print (join ",", @ARG), "\n";
}
@a = (1,2,3); @b=(4,5,6);
printIt @a, @b; # 1,2,3,4,5,6
Sometimes we want the sub to access the lists separately
This is done by passing two references to lists:
Use \@
to pass a reference
Use {$ref} to dereference reference $ref
sub sublines1{
# $refa and $refb are pointers to the actual param lists
my ($refa, $refb) = @ARG;
my @sumList = ();
foreach $i (0 .. $#{$refa}){
$a = shift @{$refa};
$b = shift @{$refb};
push @sumList, $a + $b;
}
return @sumList;
}
@a = (1,2,3,4);
@b = (5,6,7,8);
@sumlines = sublines1 \@a, \@b; # Pass references to @a and @b
print '@sumlines is ', join(", ", @sumlines), "\n";
References Allow Modifying List that is the Actual Param
@sumlines = sublines1 \@a, \@b;
print '@sumlines is ', join(", ", @sumlines), "\n";
print '@a is ', join(", ", @a), "\n";
print '@b is ', join(", ", @b), "\n\n";
- When a list is passed with a reference, changes to the
list are reflected in the actual parameter
- This is different from changing the elements of the actual parameter
What happens here?
sub sublines1{
# $refa and $refb are pointers to the actual param lists
my ($refa, $refb) = @ARG;
for (my $i=0; $i <= $#{$refa}; $i++){
$a = shift @{$refa};
$b = shift @{$refb};
$sum = $a + $b;
push @sumList, $sum;
}
return @sumList;
}
@a = (1,2,3,4); @b = (5,6,7,8);
@sumlines = sublines1 \@a, \@b;
print '@sumlines is ', join(", ", @sumlines), "\n";
Prototypes
- A prototype specifies what is to be passed to a subroutine
- Example:
sub mysub($$){...}
# call to mysub must pass 2 scalars
mysub(1,2);
Be careful with lists:
sub mysub1($@){...}
sub mysub2(@$){...}
sub mysub3($@){...}
mysub1(1,2,3,4,5);
mysub2(1,2,3,4,5);
mysub3(1,2,3,4,5);
Calling a subroutine with & keeps a prototype for that
routine from being checked
With Prototypes, References can be Passed Automatically
- Prototypes cause references to be automatically
passed
sub mysub(\@){$ref1 = shift @ARG}
@a = (1,2,3,4);
mysub(@a); automatically passes a reference