1. 基本子程序定义
#!/usr/bin/perl
use strict;
use warnings;
# 定义简单的子程序
sub greet {
my $name = shift; # 获取第一个参数
print "Hello, $name!\n";
}
# 调用子程序
greet("Alice");
greet("Bob");
# 带多个参数的子程序
sub add {
my ($a, $b) = @_; # 获取所有参数
return $a + $b;
}
my $sum = add(5, 3);
print "5 + 3 = $sum\n";
2. 带默认值的子程序
sub power {
my ($base, $exponent) = @_;
$exponent = 2 unless defined $exponent; # 默认指数为2
return $base ** $exponent;
}
print "3^2 = ", power(3), "\n"; # 使用默认值
print "3^3 = ", power(3, 3), "\n"; # 指定指数
3. 命名参数(使用哈希)
sub create_person {
my %params = @_;
my $name = $params{name} || "Unknown";
my $age = $params{age} || 0;
my $city = $params{city} || "Unknown";
return {
name => $name,
age => $age,
city => $city
};
}
# 使用命名参数调用
my $person = create_person(
name => "John",
age => 30,
city => "New York"
);
print "Name: $person->{name}\n";
print "Age: $person->{age}\n";
print "City: $person->{city}\n";
4. 基本错误处理(eval)
# 使用eval进行错误处理
sub divide_safe {
my ($a, $b) = @_;
my $result;
eval {
die "除数不能为零" if $b == 0;
$result = $a / $b;
};
if ($@) { # 检查错误
print "错误: $@";
return undef;
}
return $result;
}
my $value1 = divide_safe(10, 2);
print "10 / 2 = $value1\n" if defined $value1;
my $value2 = divide_safe(10, 0);
print "10 / 0 未成功\n" unless defined $value2;
5. 使用Carp模块改进错误报告
use Carp;
sub process_data {
my ($data) = @_;
unless (defined $data) {
croak "数据未定义!"; # 报告调用者位置的错误
}
# 或者使用警告
unless (ref $data eq 'HASH') {
carp "期望哈希引用,但得到了 " . (ref $data || '标量');
$data = {}; # 提供默认值
}
return $data->{value} || 0;
}
# 测试
eval {
process_data(undef);
};
if ($@) {
print "捕获到错误: $@";
}
6. Try::Tiny模块(现代错误处理)
use Try::Tiny;
sub risky_operation {
my ($input) = @_;
try {
die "输入无效" unless defined $input;
die "输入不能为空" if $input eq "";
# 正常处理
return uc($input);
}
catch {
my $error = $_;
print "操作失败: $error\n";
return undef;
};
}
my $result1 = risky_operation("hello");
print "结果1: $result1\n" if defined $result1;
my $result2 = risky_operation("");
print "结果2未定义\n" unless defined $result2;
7. 完整的错误处理示例
#!/usr/bin/perl
use strict;
use warnings;
use Carp;
# 配置文件读取子程序
sub read_config_file {
my ($filename) = @_;
# 参数检查
unless (defined $filename) {
croak "未指定配置文件名";
}
unless (-e $filename) {
croak "配置文件不存在: $filename";
}
unless (-r $filename) {
croak "无法读取配置文件: $filename";
}
# 尝试打开文件
open my $fh, '<', $filename
or croak "无法打开文件 $filename: $!";
my %config;
while (my $line = <$fh>) {
chomp $line;
next if $line =~ /^\s*#/; # 跳过注释
next if $line =~ /^\s*$/; # 跳过空行
if ($line =~ /^(\w+)\s*=\s*(.+)$/) {
$config{$1} = $2;
} else {
carp "忽略无效的配置行: $line";
}
}
close $fh;
return \%config;
}
# 使用示例
try {
my $config = read_config_file("config.txt");
if ($config && keys %$config) {
print "成功读取配置:\n";
foreach my $key (sort keys %$config) {
print " $key = $config->{$key}\n";
}
} else {
print "配置文件为空或无效\n";
}
}
catch {
print "错误: $_\n";
};
8. 子程序引用(高阶函数)
# 创建子程序引用
my $greeter = sub {
my $name = shift;
return "Hello, $name!";
};
print $greeter->("World"), "\n";
# 将子程序引用作为参数传递
sub apply_to_list {
my ($func, @list) = @_;
return map { $func->($_) } @list;
}
my @numbers = (1, 2, 3, 4, 5);
my $square = sub { $_[0] ** 2 };
my @squares = apply_to_list($square, @numbers);
print "平方: @squares\n";
最佳实践总结
总是使用 use strict; 和
use warnings;
参数处理:使用
shift 或
@_ 处理参数
错误检查:尽早检查参数有效性
错误报告:使用
croak 报告调用者位置的错误
资源清理:确保文件句柄等资源被正确关闭
返回值:一致地使用返回值表示成功/失败
文档化:为子程序添加注释说明参数和返回值
这些示例展示了Perl中子程序创建和错误处理的各种技术,可以根据具体需求选择合适的方法。