子程序基础 #

一、子程序概述 #

子程序(Subroutine)是Perl中可重用的代码块,使用 sub 关键字定义。

1.1 定义子程序 #

perl
sub greet {
    print "Hello, World!\n";
}

1.2 调用子程序 #

perl
greet();
&greet();

1.3 先调用后定义 #

perl
greet();

sub greet {
    print "Hello, World!\n";
}

二、参数传递 #

2.1 传递参数 #

参数存储在 @_ 数组中:

perl
sub greet {
    my $name = $_[0];
    print "Hello, $name!\n";
}

greet("Tom");

2.2 解包参数 #

推荐使用列表赋值解包:

perl
sub add {
    my ($a, $b) = @_;
    return $a + $b;
}

print add(3, 5);

2.3 多个参数 #

perl
sub print_info {
    my ($name, $age, $city) = @_;
    print "Name: $name\n";
    print "Age: $age\n";
    print "City: $city\n";
}

print_info("Tom", 25, "Beijing");

2.4 可变参数 #

perl
sub sum {
    my $total = 0;
    $total += $_ foreach @_;
    return $total;
}

print sum(1, 2, 3, 4, 5);

三、返回值 #

3.1 return语句 #

perl
sub add {
    my ($a, $b) = @_;
    return $a + $b;
}

my $result = add(3, 5);
print $result;

3.2 隐式返回 #

子程序返回最后一条语句的值:

perl
sub add {
    my ($a, $b) = @_;
    $a + $b;
}

my $result = add(3, 5);
print $result;

3.3 返回多个值 #

perl
sub min_max {
    my @arr = @_;
    my $min = $arr[0];
    my $max = $arr[0];
    
    foreach my $val (@arr) {
        $min = $val if $val < $min;
        $max = $val if $val > $max;
    }
    
    return ($min, $max);
}

my ($min, $max) = min_max(3, 1, 4, 1, 5, 9, 2, 6);
print "Min: $min, Max: $max\n";

3.4 返回列表 #

perl
sub get_numbers {
    return (1, 2, 3, 4, 5);
}

my @nums = get_numbers();
print "@nums\n";

四、调用约定 #

4.1 省略括号 #

如果子程序已定义,可省略括号:

perl
sub greet {
    print "Hello!\n";
}

greet;
greet();

4.2 &前缀 #

使用 & 前缀调用:

perl
&greet();

4.3 作为引用 #

perl
my $func_ref = \&greet;
$func_ref->();
&$func_ref();

五、子程序签名 #

Perl 5.20+ 支持签名(需启用):

perl
use v5.20;
use feature 'signatures';
no warnings 'experimental::signatures';

sub add($a, $b) {
    return $a + $b;
}

sub greet($name = "World") {
    say "Hello, $name!";
}

add(3, 5);
greet();
greet("Tom");

六、递归 #

6.1 基本递归 #

perl
sub factorial {
    my $n = shift;
    return 1 if $n <= 1;
    return $n * factorial($n - 1);
}

print factorial(5);

6.2 斐波那契数列 #

perl
sub fibonacci {
    my $n = shift;
    return $n if $n <= 1;
    return fibonacci($n - 1) + fibonacci($n - 2);
}

print fibonacci(10);

6.3 尾递归优化 #

perl
sub factorial_tail {
    my ($n, $acc) = @_;
    $acc //= 1;
    return $acc if $n <= 1;
    return factorial_tail($n - 1, $n * $acc);
}

print factorial_tail(5);

七、实践练习 #

练习1:数学函数 #

perl
#!/usr/bin/perl
use strict;
use warnings;
use v5.10;

sub max {
    my @arr = @_;
    my $max = $arr[0];
    $max = $_ > $max ? $_ : $max foreach @arr;
    return $max;
}

sub min {
    my @arr = @_;
    my $min = $arr[0];
    $min = $_ < $min ? $_ : $min foreach @arr;
    return $min;
}

sub average {
    my @arr = @_;
    my $sum = 0;
    $sum += $_ foreach @arr;
    return @arr ? $sum / @arr : 0;
}

my @numbers = (3, 1, 4, 1, 5, 9, 2, 6);
say "Max: " . max(@numbers);
say "Min: " . min(@numbers);
say "Average: " . sprintf("%.2f", average(@numbers));

练习2:字符串处理 #

perl
#!/usr/bin/perl
use strict;
use warnings;
use v5.10;

sub reverse_words {
    my $str = shift;
    my @words = split /\s+/, $str;
    return join " ", reverse @words;
}

sub count_words {
    my $str = shift;
    my @words = split /\s+/, $str;
    return scalar @words;
}

my $text = "Hello Perl World";
say "Original: $text";
say "Reversed: " . reverse_words($text);
say "Word count: " . count_words($text);

练习3:验证函数 #

perl
#!/usr/bin/perl
use strict;
use warnings;
use v5.10;

sub is_email {
    my $str = shift;
    return $str =~ /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
}

sub is_phone {
    my $str = shift;
    return $str =~ /^\d{3}-\d{3}-\d{4}$/;
}

my @tests = (
    "test@example.com",
    "invalid-email",
    "123-456-7890",
    "12345",
);

foreach my $test (@tests) {
    say "$test:";
    say "  Email: " . (is_email($test) ? "Yes" : "No");
    say "  Phone: " . (is_phone($test) ? "Yes" : "No");
}

八、总结 #

本章学习了:

  • 子程序的定义和调用
  • 参数传递(@_)
  • 返回值
  • 递归

下一章将学习参数与返回值的更多细节。

最后更新:2026-03-27