# 第八章，引用

```   @john = (47, "brown", 186);
@mary = (23, "hazel", 128);
@bill = (35, "blue",  157);
```

```   @vitals = ('john', 'mary', 'bill');
```

## 8.2 创建引用

### 8.2.1 反斜杠操作符

```\$scalarref = \\$foo;
\$constref  = \186_282.42;
\$arrayref  = \@ARGV;
\$hashref   = \%ENV;
\$coderef   = \&handler;
\$globref   = \*STDOUT;
```

### 8.2.2 匿名数据

#### 8.2.2.1 匿名数组组合器

```   \$arrayref = [1, 2, ['a', 'b', 'c', 'd']];
```

```   \$table = [ [ "john", 47, "brown", 186],
[ "mary", 23, ""hazel", 128],
[ "bill", 35, "blue", 157] ];
```

#### 8.2.2.2 匿名散列组合器

```\$hashref = {
'Clyde' => \$bonnie,
'Antony' => 'Cleo' . 'patra',
};```

```\$table = {
"john" => [47, "brown", 186],
"mary" => [23, "hazel", 128],
"bill" => [35, "blue", 157],
};```

```\$table = {
"john" => { age    => 47,
eyes   => "brown",
weight => 186,
},
"mary" => { age    => 23,
eyes   => "hazel",
weight => 128,
},
"bill" => { age    => 35,
eyes   => "blue",
weight => 157,
},
};```

```   sub hashem {      { @_ }   }   # 不声不响地错误 -- returns @_
sub hashem {      +{ @_ } }     # 对
sub hashem { return {@_}}      # 对
```

#### 8.2.2.3 匿名子过程组合器

```   \$coderef = sub { print "Boink!\n" };   # 现在 &\$coderef 打印 "Boink!"
```

### 8.2.3 对象构造器

```\$objref = Doggie::->new(Tail => 'short', Ears => 'long');  #1
\$objref = new Doggie:: Tail => 'short', Ears => 'long';       #2
\$objref = Doggie->new(Tail => 'short', Ears => 'long');    #3
\$objref = new Doggie Tail => 'short', Ears => 'long';         #4
```

### 8.2.4 句柄引用

```splutter(\*STDOUT);

sub splutter {
my \$fh = shift;
print \$fh = "her um well a hmmm\n";
}

\$rec = get_rec(\*STDIN);
sub get_rec {
my \$fh = shift;
return scalar <\$fh>;
}```

```   for \$file (@name) {
local *FH;
open(*FH, \$file) || next;
\$handle(\$file) = *FH;
}
```

```   for \$file (@name) {
my \$fh;
open(\$fh, \$file) || next;
\$handle(\$file) = \$fh;
}
```

### 8.2.5 符号表引用

```   \$scalarref = *foo{SCALAR};      # 和 \\$foo 一样
\$arrayref  = *ARGV{ARRAY};         # 和 \@ARGV 一样
\$hashref   = *ENV{HASH};      # 和 \%ENV 一样
\$coderef   = *handler{CODE};         # 和 \&handler 一样
\$globref   = *foo{GLOB};          # 和 \*foo 一样
\$ioref     = *STDIN{IO};             # ？...
```

```   splutter(*STDOUT);
splutter(*STDOUT{IO});

sub splutter {
my \$fh = shift;
print \$fh "her um well a hmmm\n";
}
```

## 8.3.1 把一个变量当作变量名使用

```   \$foo       = "three humps";
\$scalarref   = \\$foo;      # \$scalarref 现在是一个指向 \$foo 的引用
\$camel_model    = \$\$scalarref;   # \$camel_model 现在是"three humps"
```

```   \$bar =   \$\$scalarref;

push(@\$arrayref, \$filename);
\$\$arrarref[0]   = "January";                   # 设置 @\$arrayref 的第一个元素
@\$arrayref[4..6]=qw/May June July/;   # 设置若干个 @\$arrayref 的元素

%\$hashref = (KEY => "RING", BIRD => "SING");   # 初始化整个散列
\$\$hashref{KEY} = "VALUE";                     # 设置一个键字/数值对
@\$hashref{"KEY1", "KEY2"} = {"VAL1", "VAL2"};         # 再设置两对

&\$coderef(1,2,3);

print \$handleref "output\n";
```

```   \$refrefref = \\\"howdy";
print \$\$\$\$refrefref;
```

## 8.3.2 把一个 BLOCK 块当作变量名用

```   \$bar = \${\$scalarref};
push(@{\$arrayref}, \$filename);
\${\$arrayref}[0] = "January";
@{\$arrayref}[4..6] = qw/May June July/;
\${\$hashref}{"KEY"} = "VALUE";
@{\$hashref}{"KEY", "KEY2"} = ("VAL1", "VAL2");
&{\$coderef}(1,2,3);
```

```   \$refrefref = \\\"howdy";
print \${\${\${\$refrefref}}};
```

```   &{ \$dispatch{\$index} } (1, 2, 3);
```

## 8.3.3 使用箭头操作符

```   \$  \$arrayref  [2]  = "Dorian";      #1
\${ \$arrayref }[2]  = "Dorian";      #2
\$arrayref->[2]  = "Dorian";        #3

\$  \$hashref  {KEY} = "F#major";      #1
\${ \$hashref }{KEY} = "F#major";      #2
\$hashref->{KEY} = "F#major";         #3

&  \$coderef  (Presto => 192);      #1
&{ \$coderef }(Presto => 192);      #2
\$coderef->(Presto => 192);          #3
```

```print \$array[3]->{"English"}->[0];
```

```   \$array[3]->{"English"}->[0] = "January";
```

```   \$dispatch{\$index}(1, 2, 3);
\$array[3]{"English"}[0] = "January";
```

```   \$answer[\$x][\$y][\$z] += 42;
```

```   \$listref->[2][2] = "hello";      # 相当干净
\$\$listref[2][2]  = "hello";      # 有点混乱
```

```   \$listref[2]->{\$greeting} = "hello";
```

## 8.3.5 伪散列

```   \$john = [ {age => 1, eyes => 2, weight => 3}, 47, "brown", 186 ];
```

```   \$john->{weight}         # 把 \$john 当作一个 hashref 对待
\$john->[3]            # 把 \$john 当作一个 arrayref 对待
```

```   \$john->[0]{height} = 4;      # 高度是元素 4 的数值
\$john->{height} = "tall";      # 或者 \$john->[4] = "tall"
```

```   delete \$john->[9]{height};      # 只从下层散列中删除
\$john->{height};         # 现在抛出一个例外
\$john->[4];            # 仍然打印 "tall"
```

```use fields;
\$ph = fields::phash(age => 47, eyes => "brown", weight => 186);
print \$ph->(age);```

```use fields;
\$ph = fields::phash([qw(age eyes brown)], [47]);
\$ph->{eyes} = undef;

print exists \$ph->{age};      # 对，'age' 在声明中就设置了
print exists \$ph->{weight};      # 错，'weight' 还没有用呢
print exists \$ph->{eyes};      # 对，你的 'eyes' 已经修改过了```

```   print exists \$ph->[0]{age};      # 对，'age' 是一个有效的字段
print exists \$ph->[0]{name};   # 错，不能使用 'name'
```

```   print delete \$ph->{age};      # 删除并返回 \$ph->[1], 47
print exists \$ph->{age};      # 现在是错的
print exists \$ph->[0]{age};      # 对，'age' 键字仍然可用
print delete \$ph->[0]{age};      # 现在 'age' 键字没了
print \$ph->{age};         # 运行时例外
```

## 8.3.6 硬引用可以用的其他技巧

```   @reflist = (\\$s, \@a, \%h, \&f);      # 一列四个元素的表
@reflist = \(\$s, @a, %h, &f);                # 同样的东西
```

```   @reflist = \(@x);                   # 代换数组，然后获得引用
@reflist = map (\\$_) @x;         # 一样的东西
```

```   @envrefs = \@ENV{'HOME', 'TERM'};      # 反斜杠处理一个片段
@envrefs = \(\$ENV{HOME}, \$ENV{TERM} );   # 反斜杠处理一个列表
@envrefs = ( \\$ENV{HOME}, \\$ENV{TERM} );   # 一个两个引用的列表
```

```   @reflist = \fx();
@reflist = map { \\$_ } fx();      # 一样的东西

@reflist = \( fx(), fy(), fz() );
@reflist = ( \fx(), \fy(), fz() );      # 一样的东西
@reflist = map { \\$_ } fx(), fy(), fz();   # 一样的东西
```

```   @reflist = \localtime();         # 引用九个时间元素中的每一个
@lastref = \localtime();         # 引用的是它是否为夏时制
```

```   \$dateref = \scalar localtime();      # \"Thu Apr 19 22:02:18 2001"
```

```   sub sum {
my \$arrayref = shift;
warn "Not an array reference" if ref(\$arrayref) ne "ARRAY";
return eval join("+", @\$arrayref);
}
```

```   print "My sub returned @{[ mysub(1, 2, 3) ]} that time.\n";
```

```   print "We need @{ [\$n + 5] } widgets!\n";
```

```   print "Mysub return @{ [scalar mysub(1,2,3)] } now.\n";
```

## 8.3.7 闭合（闭包）

```   {
my \$critter = "camel";
\$critterref = \\$critter;
}
```

\$\$critterref 的数值仍将是“camel”，即使在离开闭合的花括弧之后 \$critter 消失了也如此。 但是 \$critterref 也可以指向一个指向了 \$critter 的子过程：

```   {
my \$critter = "camel";
\$critterref = sub { return \$critter };
}
```

```sub make_saying {
my \$salute = shift;
my \$newfunc = sub {
my \$target = shift;
print "\$salute, \$target!\n";
};
return \$newfunc;      # 返回一个闭合
}

\$f = make_saying("Howdy");      # 创建一个闭合
\$g = make_saying("Greetings");   # 创建另外一个闭合

# 到时...

\$f->("world");
\$g->("earthings");```

```   Howdy, world!
Greetings earthlings!
```

Perl 并不给对象方法（在第十二章描述）提供引用，但是你可以使用闭合获取类似的效果。假设 你需要这么一个引用：它不仅仅指向他代表的方法的子过程，而且它在调用的时候，还会在特定的 对象上调用该方法。你可以很方便地把对象和方法都看成封装在闭合中的词法变量：

```sub get_method {
my (\$self, \$methodname) = @_;
my \$methref = sub {
# 下面的 @_ 和上面的那个不一样！
return \$self->\$methodname(@_);
};
return \$methref;
}

my \$dog = new Doggie::
Name => "Lucky",
Legs => 3,
Tail => "clipped";

our \$wagger = get_method_ref(\$dog, 'wag');
\$wagger->("tail");      # 调用 \$dog->wag('tail').```

### 8.3.7.1 用闭合做函数模板

```   print "Be", red("careful"), "with that ", green("light), "!!!";
```

red 和 green 函数会非常类似。我们已经习惯给我们的函数名字，但是闭合没有名字，因为它们 只是带倾向性的匿名子过程。为了绕开这个问题，我们将使用给我们的匿名子过程命名的非常技巧 性的方法。你可以把一个 coderef 绑定到一个现存的名字上，方法是把它赋予一个类型团，该 类型团的名字就是你想要的函数。（参阅第十章，包，里的“符号表”一节。在本例中，我们将把 它绑定到两个不同的名字上，一个是大写，一个是小写。

```@colors = qw(red blue green yellow orange purple wiolet);
for my \$name (@colors) {
no strict 'refs';      # 允许符号引用
*\$name = *(uc \$name) = sub { "<FONT COLOR='\$name'7gt;@_</FONT>" };
}```

```   *\$name = sub (\$) {"\$_[0]" };
```

### 8.3.7.2 嵌套的子过程

```sub outer {
my \$x = \$_[0] + 35;
local *inner = sub { return \$x * 19};
return \$x + inner();
}```

```sub outer {
my \$x = \$_[0] + 35;
my \$inner = sub {return \$x * 19 };
return \$x + \$inner->();
}```

## 8.4 符号引用

```   \$name = "bam";
\$\$name = 1;         # 设置 \$bam
\$name->[0] = 4;      # 设置 @bam 的第一个元素
\$name->{X} = "Y";      # 设置 %bam 的 X 元素为 Y
@\$name = ();         # 清除 @bam
keys %\$name;         # 生成 %bam 的键字
&\$name;         # 调用 &bam
```

```   use strict 'refs';
```

```   no strict 'refs';
```

```   \${identifier};      # 和 \$identifier 一样
\${"identifier"};       # 也是 \$identifier，不过却是一个符号引用。
```

```our \$value = "global";
{
my \$value = "private";
print "Inside, mine is \${value},";
print "but ours is \${'value'}.\n";
}
print "Outside, \${value} is again \${'value'}.\n";```

```   Inside, mine is private, but ours is global.
Outside, global is again global.
```

## 8.5 花括弧，方括弧和引号

```   \$push = "pop on ";
print "\${push}over";
```

```   print \${push} . 'over';
```

```   print \${ push } . 'over';
```

```   \$hash{ "aaa" }{ "bbb" }{ "ccc" }
```

```   \$hash{ aaa}{ bbb }{ ccc }
```

```   \$hash{aaa}{bbb}{ccc}
```

```   \$hash{ shift }
```

```   \$hash{ shift() }
\$hash{ +shift }
\$hash{ shift @_ }
```

### 8.5.1 引用不能当作散列键字用

```   \$x{ \\$a } = \$a;
(\$key, \$value) = each %x;
print \$\$key;         # 错误
```

```   \$r = \@a;
\$x{ \$r } = \$r;
```

```use Tie::RefHandle;
tie my %h, 'Tie::RefHash';
%h = (
["this", "here"]    => "at home",
["that", "there"]    => "elsewhere",
);

while ( my(\$keyref, \$value) = each %h ) {
print "@\$keyref is \$value\n";
}```

### 8.5.2 垃圾收集，循环引用和弱引用

```   {      # 令 \$a 和 \$b 指向对方
my (\$a, \$b);
\$a = \\$b;
\$b = \\$a;
}
```

```   {      # 令 \$a 指向它自己
my \$a;
\$a = \\$a;
}
```

to top