Perl子程序@_操作、标量上下文及循环变量相关问题
Perl 平均值子程序常见问题解答
问题复现代码
sub average { my $uebergeben = (@_); my $verrechnen = 0; foreach my $item (@_){ $verrechnen += $item; } my $scalardef = $verrechnen; my $average = $scalardef/$uebergeben; print "$average\n"; } my @array = 20; my @hallo = 10; average(@array,@hallo);
问题1:为什么无法直接操作@_数组?为什么遍历生成的$item没有将传入值作为标量传递?
不存在无法直接操作@_数组的限制,@_是Perl子程序内部可读写的内置参数数组,支持所有标准数组操作。产生这个误解的核心原因是对Perl的上下文机制不熟悉:
- 在列表上下文操作
@_时,返回的是所有传入参数组成的列表;在标量上下文操作@_时,返回的是传入参数的总个数,不是参数值本身。 - 用
foreach my $item (@_)遍历数组时,$item本身就是标量变量,会依次指向@_中的每一个元素,默认是传引用行为(在循环内修改$item会直接改动@_里对应的原元素),完全是标量上下文操作。示例代码中调用average(@array,@hallo)时,传入的两个数组会被自动打散为(20,10)的列表存入@_,遍历得到的$item依次为20、10,逻辑上没有问题。
问题2:为什么求和变量$verrechnen必须定义在foreach循环的外部?
这是Perl块级作用域的规则决定的:如果在foreach循环块内部用my声明$verrechnen,每次循环迭代都会重新创建一个初始值为0的全新变量,上一轮累加的结果会直接丢失;且循环结束后该变量会超出作用域无法访问,根本无法得到所有元素的累加总和。只有把累加变量定义在循环外部,整个循环过程操作的是同一个变量,才能完成跨迭代的累加逻辑。
问题3:为什么无法直接使用$verrechnen += (@_)的写法?为什么遍历得到的$item不处于标量上下文?
$verrechnen += (@_)语法上不报错,但逻辑完全错误:+=运算符要求左侧为标量变量,右侧表达式会被强制置于标量上下文。而数组在标量上下文下的返回值是数组的元素长度,不是元素值的总和——比如传入2个参数时,这行代码等价于$verrechnen += 2,完全达不到求和效果。
另外foreach遍历得到的$item天然处于标量上下文:数组的单个元素本身就是标量值,循环变量依次取每个元素的过程本身就是标量赋值,不存在上下文不匹配的问题。
生产环境简化写法提示:可以直接引入核心模块
List::Util的sum函数完成求和,无需手动写循环:use List::Util qw(sum); my $total = sum(@_);
内容的提问来源于stack exchange,提问作者grotivanhelden
相关产品推荐
相关产品推荐

