路过

通过复制或引用分配任务?
在分配时,perl
通过值复制,参见以下代码
my $var1 = "not modified";
my $var2 = $var1; # Copy the content, not the variable itself
$var1 = "modified";
print "$var2\n";
此代码将打印初始的未修改值
not modified
这是因为赋值是复制变量的内容,而不是别名变量。
要检查变量的标识,我们应该检查它们的引用。
关于引用的说明
一个引用表示一个包含值存储位置信息的标量值(某种程度上)。拥有相同的存储位置意味着它是相同的变量(或别名)。引用的字符串表示形式甚至给出了它所指向的值的类型(标量、数组、哈希等)的详细信息。就像下面刚刚看到的SCALAR(...)
SCALAR(0x...)
如果你只对指向的类型感兴趣,你可以通过ref
运算符获取这些信息(例如,用于类型检查)
my @array = ();
print ref(\@array) . "\n";
这将给出
ARRAY
你可以通过反斜杠运算符和引号(例如$
)或->
来获取(创建)引用
my $str = "foo";
# Get reference
my $ref = \$str;
# Dereference with $
print "$$ref\n";
或者
my @array = ( "foo", "bar", "baz" );
# Get reference
my $ref = \@array;
# Derefrence with ->
print "$ref->[0]\n";
两者都将打印
foo
你也可以通过符号引用来通过其名称构建引用
my $name = "var";
# Store in $var
$$name = "bazinga";
print "$var\n";
这将打印
bazinga
或者,你可以使用类似这样的“匿名”运算符[]
或{}
my @characters1 = ( "Sheldon", "Leonard", "Penny" );
my @characters2 = ( "Howard", "Rajesh", "Bernadette", "Amy" );
my @big_bang_theory = ();
push @big_bang_theory, [@characters1];
push @big_bang_theory, [@characters2];
这样,数组引用被推入,但它指向的是新数组(因此它打破了@characters1
/@characters2
和@big_bang_theory
内容之间的联系)。
或者,你甚至可以获取一个引用的引用,然后多次取消引用。这允许你产生奇怪/愚蠢的东西,如下所示
my $str = "bazinga";
# Get a ref on a ref on a ref on a...
my $rrrrrrrrrref = \\\\\\\\\\$str;
# De-de-de-...-de-dereference
print "$$$$$$$$$$$rrrrrrrrrref\n";
或者
my $str = "bazinga";
# De-de-de-...-de-dereference a re-re-re-re-...-ref
print $$$$$$$$$${\\\\\\\\\\$str} . "\n";
但不要这样做。
检查引用
回到我们的变量,你可以通过打印它们的引用来检查它们的“身份”
# Compare storage location
print \$var1 . " vs " . \$var2 . "\n";
这给出了2个不同的存储位置
SCALAR(0x55589c6378e0) vs SCALAR(0x55589c6378f8)
^^ ^^
因此,它清楚地表明这些是两个不同的变量。
但是等等,还有一些情况下赋值实际上是别名的,这就是在foreach
循环中发生的情况
my @array = ( "foo", "bar", "baz" );
foreach my $var (@array) {
$var = "modified"; # Modify the initial @array
}
print "@array\n";
然后数组就得到了很好的修改
modified modified modified
当我们讨论“检查引用”的话题时,你可以使用像Devel::Peek这样的模块或类似工具来获取引用(或任何变量)的内部细节。你将得到比你想要的更多的信息!
按引用传递还是按值传递?
我们已经讨论了赋值,但参数是如何传递给子例程的?
答案:它们始终是“按引用传递”,但最好说“别名”,以避免与之前讨论的引用概念混淆。
这个事实远非显而易见,因为你将在下一个示例中看到,将参数检索到新变量中(因此是复制赋值)通常会让你相信Perl是“按值传递”的。
子例程中的变量修改
在子例程内部,根据你获取/使用参数的方式,修改变量通常不会影响参数。
sub bycopy {
my $cp = shift; # /!\ copy is done here (not during call) from aliased argument(s)
# my $cp = (@_); # Same with this
$cp = "modified";
}
my $var = "not modified";
bycopy($var);
print "$var\n";
这将打印
not modified
修改在外部不可见,因为为了传播更改,它应该被返回给调用者return $cp;
并且通过$var = bycopy($var)
赋值(实际上由于“默认变量魔法”,返回是可选的)。
显然,这种限制通常是个好主意(想想副作用),但如果你有意在调用者作用域中修改变量,则不是这样。
为了能够在子例程内部修改变量,使其传递给调用者,我可以使用引用。
sub byref {
my $ref = shift;
$$ref = "modified";
}
my $variable = "not modified";
byref(\$variable);
print "$variable\n";
这将打印
modified
这样,通过一些技巧,我可以通过引用传递变量本身,无论它是什么(标量、数组、散列……),我可以编辑它,并且它会保持不变。
但有一个更简单且明显的方法,默认数组 @_
实际上是已经别名了!
sub bydefaultarray {
$_[0] = "modified";
}
my $variable = "not modified";
bydefaultarray($variable);
print "$variable\n";
并且它已经被正确修改了
modified
你只需要直接在 @_
(或小心制作的别名)上工作,不要使用像 shift
或任何只会给你 副本 的赋值操作。
使用原型的引用隐式转换
然后,在子例程和引用周围还有一些东西需要了解:原型的使用。原型改变了 Perl 的行为,并允许例如子例程接收一个数组并将其隐式转换为引用。
以下示例强制将数组转换为引用而不是将其展开
sub proto(\@) {
my $ref = shift; # It is the array ref, not the first item of @array
print "$ref\n";
$ref->[0] = "dog";
$ref->[1] = "cat";
$ref->[2] = "pig";
}
my @array = ("foo", "bar", "baz");
# Array flattening won't occur but instead a reference will be passed
proto(@array); # proto(\@array);
print "@array\n";
输出如下
ARRAY(0x55ac34f261c0)
dog cat pig
别名
有方法可以在不系统重建引用的情况下别名变量。例如,你可以使用模块 Data::Alias
use Data::Alias;
my $variable = "not modified";
# Alias!
alias $alias = $variable;
$variable = "modified";
print "$alias\n";
并且打印得很好
modified
最近的 Perl 也带来了一些 别名功能(无需处理符号表)
use feature qw/refaliasing declared_refs/;
no warnings qw/experimental::refaliasing experimental::declared_refs/;
my $variable = "not modified";
my \$alias = \$variable; # Assigning to reference
$variable = "modified";
print "$alias\n";
这将打印
modified
并且打印的引用值进行比较
print \$alias . " equals " . \$variable . "\n";
并且它们指向同一件事情
SCALAR(0x5637ffc833a0) equals SCALAR(0x5637ffc833a0)
别名也是性能问题,这些技巧被大量用于此目的。
最近的 Perl 带来了 Copy-On-Write 功能,请参阅 perl 5.20.0 性能增强 和 perlguts COW
多亏了 COW,当我们赋值时,我们得到两个不同的变量,但只有在需要时才实际复制值,这可能产生显著的性能提升。
标签
反馈
这篇文章有什么问题吗?通过在 GitHub 上打开问题或拉取请求来帮助我们。