我试图弄清楚Perl子程序及其工作原理.从perlsub我知道子例程是按引用调用的,并且需要一个赋值(例如my(@copy) = @_;)来将它们转换为按值调用.
在下文中,我看到它change被引用称为"a"和"b"变为"x"和"y".但我很困惑为什么数组没有用额外的元素"z"扩展?
use strict;
use Data::Dumper;
my @a = ( "a" ,"b" );
change(@a);
print Dumper(\@a);
sub change
{
@_[0] = "x";
@_[1] = "y";
@_[2] = "z";
}
Run Code Online (Sandbox Code Playgroud)
输出:
$VAR1 = [
'x',
'y'
];
Run Code Online (Sandbox Code Playgroud)
在下面,我传递哈希而不是数组.为什么键不能从"a"变为"x"?
use strict;
use Data::Dumper;
my %a = ( "a" => "b" );
change(%a);
print Dumper(\%a);
sub change
{
@_[0] = "x";
@_[1] = "y";
}
Run Code Online (Sandbox Code Playgroud)
输出:
$VAR1 = {
'a' => 'y'
};
Run Code Online (Sandbox Code Playgroud)
我知道真正的解决方案是使用引用传递数组或哈希\@,但我想完全理解这些程序的行为.
ike*_*ami 39
Perl总是通过引用传递.只是有时呼叫者通过了临时标量.
你必须要意识到的第一件事是subs的参数可以是唯一的:scalars列表.*一个不能传递数组或哈希值.评估数组和散列,返回其内容列表.这意味着
f(@a)
Run Code Online (Sandbox Code Playgroud)
是相同的
f($a[0], $a[1], $a[2])
Run Code Online (Sandbox Code Playgroud)
Perl通过引用传递.具体来说,Perl将每个参数别名化为元素@_.修改元素@_将更改返回的标量$a[0]等,从而修改元素@a.
第二个重要的是数组或哈希元素的键确定元素在结构中的存储位置.否则,$a[4]并且$h{k}需要查看数组或散列的每个元素以找到所需的值.这意味着密钥不可修改.移动值需要使用新键创建新元素并删除旧键上的元素.
因此,每当您获得数组或散列的键时,您都会获得密钥的副本.新鲜的标量,可以这么说.
回到问题,
f(%h)
Run Code Online (Sandbox Code Playgroud)
是相同的
f(
my $k1 = "a", $h{a},
my $k2 = "b", $h{b},
my $k2 = "c", $h{c},
)
Run Code Online (Sandbox Code Playgroud)
@_仍然是别名的返回值%h,但其中一些只是用于持有密钥的临时标量.改变这些将不会产生持久影响.
* - 一些内置插件(例如grep)更像是流控制语句(例如while).它们具有自己的解析规则,因此不限于子的传统模型.
** - 原型可以影响参数列表的计算方式,但它仍然会产生标量列表.
Perl的子例程接受参数作为标量的平面列表.作为参数传递的数组实际上也是一个平面列表.即使哈希被视为一个键的平面列表,后跟一个值,后跟一个键,等等.
除非您明确说明,否则不会将平面列表作为参考传递.修改$_[0]修改的事实$a[0]是因为@_作为参数传递的元素的别名元素.修改$_[0]与$a[0]示例中的修改相同.但是,虽然这与适用于任何编程语言的"通过引用传递"的常见概念大致相似,但这并不是专门传递Perl引用; Perl的引用是不同的(实际上"引用"是一个重载的术语).别名(在Perl中)是某事物的同义词,其中作为引用类似于指向某事物的指针.
正如perlsyn所说,如果你@_作为一个整体分配,你就会破坏它的别名状态.另请注意,如果您尝试修改$_[0],$_[0]恰好是文字而不是变量,您将收到错误.在另一方面,修改$_[0]不修改调用者的值,如果它是可以修改的.因此在示例一中,更改$_[0]并$_[1]传播回,@a因为每个元素@_都是每个元素的别名@a.
你的第二个例子有点棘手.散列键是不可变的.除了删除散列键之外,Perl不提供修改散列键的方法.这意味着这$_[0]是不可修改的.当您尝试修改$_[0]Perl时,无法遵守该请求.它可能应该发出警告,但事实并非如此.你看,传递给它的平面列表包括不可修改的密钥,后跟可修改的值等.这主要是一个非问题.我想不出有任何理由以你演示的方式修改哈希的各个元素; 因为哈希没有特定的顺序,所以你不能简单地控制哪些元素@_传播回哪些元素%a.
正如您所指出的,正确的协议是通过\@a或\%a,因此它们可以被称为$_[0]->{element}或$_[0]->[0].尽管符号稍微复杂一点,但它在一段时间后变成了第二天性,而且(在我看来)对于正在发生的事情更加清晰.
请务必查看perlsub文档.特别是:
传入的任何参数都会显示在数组中
@_.因此,如果您使用两个参数调用函数,那么它们将存储在$_[0]和中$_[1].该数组@_是一个本地数组,但其元素是实际标量参数的别名.特别是,如果$_[0]更新了元素,则更新相应的参数(如果不可更新,则会发生错误).如果参数是调用函数时不存在的数组或散列元素,则仅在修改它时(或者如果)修改该元素或者对其进行引用.(Perl的某些早期版本创建了元素,无论元素是否已分配给.)分配给整个数组@_会删除该别名,并且不会更新任何参数.
(请注意,这use warnings比 更重要use strict。)
@_本身不是对任何东西的引用,它是一个数组(实际上,只是堆栈的一个视图,但如果您执行诸如引用它之类的操作,它会变成一个真正的数组),其元素每个都是一个别名传递参数。那些传递的参数是传递的单个标量;没有传递数组或散列的概念(尽管您可以传递对一个的引用)。
因此,移位、拼接、添加的附加元素等@_不会影响传递的任何内容,尽管它们可能会更改数组的索引或从数组中删除原始别名之一。
所以在你调用的地方change(@a),这会在堆栈上放置两个别名,一个 to$a[0]和一个 to $a[1]。 change(%a)比较复杂;%a展平为键和值的交替列表,其中值是实际的哈希值,修改它们会修改存储在哈希中的内容,但键只是副本,不再与哈希相关联。
| 归档时间: |
|
| 查看次数: |
17218 次 |
| 最近记录: |