作用域与闭包 #

一、变量作用域 #

1.1 词法作用域 my #

my 声明词法变量,作用域限于当前块:

perl
my $global = "global";

{
    my $local = "local";
    print $global;
    print $local;
}

print $global;

1.2 块作用域 #

perl
my $x = 10;

{
    my $x = 20;
    print "Inside: $x\n";
}

print "Outside: $x\n";

1.3 循环作用域 #

perl
for my $i (1..5) {
    print $i . "\n";
}

1.4 子程序作用域 #

perl
sub outer {
    my $x = 10;
    
    sub inner {
        print $x;
    }
    
    inner();
}

outer();

二、my、local、our #

2.1 my - 词法变量 #

perl
my $var = "lexical";
  • 创建新的词法变量
  • 作用域限于当前块
  • 最常用的声明方式

2.2 local - 动态作用域 #

perl
our $global = "original";

sub show {
    print $global . "\n";
}

show();

{
    local $global = "temporary";
    show();
}

show();

2.3 our - 包变量 #

perl
our $PACKAGE_VAR = "package";

sub show_var {
    our $PACKAGE_VAR;
    print $PACKAGE_VAR . "\n";
}

2.4 三者对比 #

关键字 作用域 说明
my 词法 创建新变量,块内有效
local 动态 临时修改全局变量
our 声明包变量

三、特殊变量作用域 #

3.1 临时修改特殊变量 #

perl
{
    local $/ = undef;
    my $content = <DATA>;
    print $content;
}

__DATA__
Line 1
Line 2
Line 3

3.2 临时修改分隔符 #

perl
my @arr = (1, 2, 3);

{
    local $" = " | ";
    print "@arr\n";
}

print "@arr\n";

3.3 临时修改警告 #

perl
{
    no warnings 'uninitialized';
    my $result = $undefined_var;
}

四、闭包 #

4.1 什么是闭包 #

闭包是捕获外部变量的子程序:

perl
sub make_counter {
    my $count = 0;
    
    return sub {
        return ++$count;
    };
}

my $counter = make_counter();
print $counter->() . "\n";
print $counter->() . "\n";
print $counter->() . "\n";

4.2 闭包捕获变量 #

perl
sub make_multiplier {
    my $factor = shift;
    
    return sub {
        my $num = shift;
        return $num * $factor;
    };
}

my $double = make_multiplier(2);
my $triple = make_multiplier(3);

print $double->(5) . "\n";
print $triple->(5) . "\n";

4.3 多个闭包共享变量 #

perl
sub make_account {
    my $balance = shift // 0;
    
    return {
        deposit => sub {
            my $amount = shift;
            $balance += $amount;
            return $balance;
        },
        withdraw => sub {
            my $amount = shift;
            $balance -= $amount if $balance >= $amount;
            return $balance;
        },
        balance => sub {
            return $balance;
        },
    };
}

my $account = make_account(100);
print $account->{balance}() . "\n";
print $account->{deposit}(50) . "\n";
print $account->{withdraw}(30) . "\n";

4.4 循环中的闭包 #

perl
my @funcs;

for my $i (1..5) {
    push @funcs, sub {
        return $i * 2;
    };
}

foreach my $func (@funcs) {
    print $func->() . "\n";
}

五、state变量 #

5.1 基本用法 #

state 声明持久化变量(Perl 5.10+):

perl
use v5.10;

sub counter {
    state $count = 0;
    return ++$count;
}

say counter();
say counter();
say counter();

5.2 state vs my #

perl
use v5.10;

sub with_my {
    my $count = 0;
    $count++;
    return $count;
}

sub with_state {
    state $count = 0;
    $count++;
    return $count;
}

say "my: " . with_my();
say "my: " . with_my();
say "state: " . with_state();
say "state: " . with_state();

5.3 state数组/哈希 #

perl
use v5.10;

sub get_cache {
    state %cache;
    return \%cache;
}

my $cache = get_cache();
$cache->{a} = 1;
$cache->{b} = 2;

my $cache2 = get_cache();
say $cache2->{a};

六、实用模式 #

6.1 单例模式 #

perl
{
    my $instance;
    
    sub get_instance {
        return $instance //= bless {}, __PACKAGE__;
    }
}

6.2 配置缓存 #

perl
use v5.10;

sub get_config {
    state $config;
    
    return $config if $config;
    
    $config = {
        host => "localhost",
        port => 3306,
    };
    
    return $config;
}

6.3 工厂函数 #

perl
sub create_validator {
    my %rules = @_;
    
    return sub {
        my $data = shift;
        my @errors;
        
        foreach my $field (keys %rules) {
            my $rule = $rules{$field};
            unless ($data->{$field} =~ $rule) {
                push @errors, "Invalid $field";
            }
        }
        
        return @errors;
    };
}

my $validate = create_validator(
    email => qr/^[\w.]+@[\w.]+$/,
    phone => qr/^\d{3}-\d{3}-\d{4}$/,
);

my @errors = $validate->({
    email => "test@example.com",
    phone => "123-456-7890",
});

七、实践练习 #

练习1:计数器 #

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

sub make_counter {
    my $start = shift // 0;
    my $count = $start;
    
    return {
        next => sub { return $count++; },
        reset => sub { $count = $start; return $count; },
        current => sub { return $count; },
    };
}

my $counter = make_counter(10);
say $counter->{next}();
say $counter->{next}();
say $counter->{current}();
$counter->{reset}();
say $counter->{next}();

练习2:配置管理 #

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

{
    my %config;
    
    sub set_config {
        my %args = @_;
        %config = (%config, %args);
    }
    
    sub get_config {
        my $key = shift;
        return $key ? $config{$key} : %config;
    }
}

set_config(host => "localhost", port => 3306);
set_config(debug => 1);

say "Host: " . get_config("host");
say "Port: " . get_config("port");
say "Debug: " . get_config("debug");

练习3:事件系统 #

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

sub create_emitter {
    my %events;
    
    return {
        on => sub {
            my ($event, $callback) = @_;
            push @{$events{$event}}, $callback;
        },
        emit => sub {
            my ($event, @args) = @_;
            $_->(@args) foreach @{$events{$event} // []};
        },
    };
}

my $emitter = create_emitter();

$emitter->{on}("greet", sub {
    my $name = shift;
    say "Hello, $name!";
});

$emitter->{on}("greet", sub {
    my $name = shift;
    say "Welcome, $name!";
});

$emitter->{emit}("greet", "Tom");

八、总结 #

本章学习了:

  • 词法作用域(my)
  • 动态作用域(local)
  • 包变量(our)
  • 闭包的概念和应用
  • state变量

下一章将学习文件操作。

最后更新:2026-03-27