Functions
## Learning Objectives
- Create and call functions (subroutines)
- Pass parameters and return values
- Understand wantarray for context
- Master closures and anonymous functions
- Use prototyped functions
## Subroutines
### Declaration
```perl
use strict;
use warnings;
sub greet {
print "Hello!\n";
}
greet(); # Call the subroutine
```
### Naming
```perl
sub say_hello { }
sub say_hello_world { }
sub with_underscore { }
sub mixedCase { } # Perl convention is snake_case
```
### Calling
```perl
# With parentheses
greet();
greet;
# With arguments
greet("Alice");
greet "Alice"; # Without parens
```
## Parameters
### @_ Array
```perl
use strict;
use warnings;
sub greet {
my ($name) = @_; # Unpack parameters
print "Hello, $name!\n";
}
greet("Alice"); # Hello, Alice!
```
### Multiple Parameters
```perl
sub add {
my ($a, $b) = @_;
return $a + $b;
}
print add(5, 3); # 8
```
### Flexible Parameters
```perl
sub sum {
my $total = 0;
foreach my $val (@_) {
$total += $val;
}
return $total;
}
print sum(1, 2, 3, 4, 5); # 15
```
### Named Parameters
```perl
sub create_user {
my %params = @_;
my $name = $params{name} // "Anonymous";
my $age = $params{age} // 0;
return { name => $name, age => $age };
}
my $user = create_user(name => "Alice", age => 30);
```
### Default Parameters
```perl
sub greet {
my ($name, $greeting) = @_;
$greeting //= "Hello";
print "$greeting, $name!\n";
}
greet("Alice"); # Hello, Alice!
greet("Bob", "Hi"); # Hi, Bob!
```
## Return Values
### Explicit return
```perl
sub max {
my ($a, $b) = @_;
if ($a > $b) {
return $a;
}
return $b;
}
print max(5, 3); # 5
```
### Implicit Return
```perl
sub get_values {
my @values = (1, 2, 3);
return @values; # List context
}
my @nums = get_values(); # (1, 2, 3)
sub get_first {
my ($a, $b) = @_;
$a; # Last expression returned
}
my $first = get_first(5, 3); # 5
```
### Returning Multiple Values
```perl
sub divide {
my ($a, $b) = @_;
return (int($a / $b), $a % $b);
}
my ($quotient, $remainder) = divide(10, 3);
print "Q: $quotient, R: $remainder\n"; # Q: 3, R: 1
```
## Context Awareness
### wantarray
```perl
sub get_values {
if (wantarray) {
return ("list", "context");
} elsif (defined wantarray) {
return "scalar";
} else {
return "void";
}
}
my @list = get_values(); # "list context"
my $scalar = get_values(); # "scalar"
get_values(); # "void"
```
### Practical Example
```perl
sub get_data {
my @data = (1, 2, 3);
if (wantarray) {
return @data;
}
return \@data; # Return reference in scalar context
}
my @list = get_data(); # List
my $ref = get_data(); # Reference
```
## Passing References
### Array References
```perl
sub process_array {
my ($arr_ref) = @_;
foreach my $elem (@$arr_ref) {
print "$elem ";
}
print "\n";
}
my @nums = (1, 2, 3);
process_array(\@nums);
```
### Hash References
```perl
sub process_hash {
my ($hash_ref) = @_;
foreach my $key (keys %$hash_ref) {
print "$key => $hash_ref->{$key}\n";
}
}
my %data = (a => 1, b => 2);
process_hash(\%data);
```
### Modify Original
```perl
sub double_values {
my ($arr_ref) = @_;
for my $i (0 .. $#$arr_ref) {
$arr_ref->[$i] *= 2;
}
}
my @nums = (1, 2, 3);
double_values(\@nums);
print "@nums\n"; # 2 4 6
```
## Closures
### Basic Closure
```perl
use strict;
use warnings;
sub create_counter {
my $count = 0;
return sub {
$count++;
return $count;
};
}
my $counter = create_counter();
print $counter->(); # 1
print $counter->(); # 2
print $counter->(); # 3
my $counter2 = create_counter();
print $counter2->(); # 1 (separate count)
```
### Factory Function
```perl
sub multiplier {
my ($factor) = @_;
return sub {
my ($num) = @_;
return $num * $factor;
};
}
my $double = multiplier(2);
my $triple = multiplier(3);
print $double->(5); # 10
print $triple->(5); # 15
```
### State Variables (Perl 5.10+)
```perl
use strict;
use warnings;
use 5.010;
sub counter {
state $count = 0; # Persistent state
return ++$count;
}
print counter(); # 1
print counter(); # 2
print counter(); # 3
```
## Anonymous Functions
### Closures as Callbacks
```perl
use strict;
use warnings;
my @numbers = (1, 2, 3, 4, 5);
my @squares = map { $_ * $_ } @numbers;
print "@squares\n"; # 1 4 9 16 25
my @evens = grep { $_ % 2 == 0 } @numbers;
print "@evens\n"; # 2 4
```
### Anonymous Subroutine
```perl
my $add = sub {
my ($a, $b) = @_;
return $a + $b;
};
print $add->(5, 3); # 8
# Or with dereferencing
print &{ $add }(5, 3); # 8
```
## Prototypes
### Basic Prototypes
```perl
use strict;
use warnings;
sub my_print($$) { # Expects two scalars
my ($str1, $str2) = @_;
print $str1, $str2;
}
my_print("Hello", " World");
```
### Prototype Characters
| Char | Meaning |
|------|---------|
| `$` | Scalar |
| `@` | Array |
| `%` | Hash |
| `&` | Subroutine |
| `*` | Typeglob |
| `\` | Reference |
### Problems with Prototypes
```perl
# Prototypes don't work with method calls
# and can be confusing. Use with caution.
# Better: explicit parameters
sub add {
my ($a, $b) = @_;
return $a + $b;
}
```
## Special Variables in Subroutines
### @_ Details
```perl
sub inspect_args {
print "Number of args: ", scalar(@_), "\n";
foreach my $i (0 .. $#_) {
print " Arg $i: $_[$i]\n";
}
}
inspect_args(1, "hello", 3.14);
```
### Shifting
```perl
sub process {
my $first = shift; # First element
my $second = shift; # Second element
my @rest = @_; # Remaining elements
}
```
## Recursion
```perl
sub factorial {
my ($n) = @_;
return 1 if $n <= 1;
return $n * factorial($n - 1);
}
print factorial(5); # 120
# Iterative version
sub factorial_iter {
my ($n) = @_;
my $result = 1;
for my $i (2 .. $n) {
$result *= $i;
}
return $result;
}
```
## Summary
- Subroutines defined with `sub name { }`
- Parameters passed via `@_` array
- Use `my` to localize and unpack parameters
- Return values with `return` or implicit last expression
- `wantarray` checks calling context
- Closures capture lexical variables
- Anonymous subroutines with `sub { }`
- References passed for arrays/hashes to modify
- `state` variables persist between calls (5.10+)
- Prototypes are optional and can be problematic
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →