我有一个更高阶的函数,它甚至可以映射数组中的位置值:
sub map_even(&@) {
my $block = shift;
my @res;
for $i (0..$#_) {
push @res, $i%2 ? $_[$i] : &$block($_[$i]);
}
@res;
}
print map_even {$_*$_} 1,2,3,4;
我希望输出为14316
,但实际输出为
0204
为什么会发生这种情况,我该如何解决这个问题?是否可以对代码进行任何改进?
答案 0 :(得分:4)
在您的匿名函数中,您必须通过$_[0]
(提示:@_
数组)访问第一个输入参数。
use strict;
use warnings;
sub map_even(&@) {
my $block = shift;
my @res;
for my $i (0..$#_) {
push @res, $i%2 ? $block->($_[$i]) : $_[$i];
}
@res;
}
print join ",", map_even {$_[0]*$_[0]} 1,2,3,4;
输出
1,4,3,16
使用$_
,
sub map_even(&@) {
my $block = shift;
my @res;
for my $i (0..$#_) {
push @res, $i%2 ? $block->() : $_ for $_[$i];
# or
# local $_ = $_[$i];
# push @res, $i%2 ? $block->() : $_;
}
@res;
}
print join ",", map_even {$_*$_} 1,2,3,4;
答案 1 :(得分:3)
在map_even
块中,您使用特殊的$_
变量。但是,您必须在循环中设置它:
local $_ = $_[$i];
... $block->();
$_
是一个全局变量,可以使用local
运算符暂时覆盖。 $_
与子例程参数无关。
关于别名:Perls for
,map
和grep
主要将$_
别名为当前元素作为性能黑客,而不是因为这种行为特别需要。为了执行别名,您应该本地化包含*_
变量的整个$_
typeglob,然后将别名目标的标量引用分配给glob:
local *_ = \$_[$i];
答案 2 :(得分:2)
我会解决这两种方式之一。
首先,使用List::Utils
的pairmap
:
use strict;
use warnings;
use List::Util qw(pairmap);
my @x = (1 .. 4);
my @result = pairmap {$a, $b**2} @x;
print "@result\n";
或者更简单地说,只需使用索引:
use strict;
use warnings;
my @x = (1 .. 4);
my @result = map {$_ % 2 ? $x[$_] ** 2 : $x[$_]} (0..$#x);
print "@result\n";
然而,如果你真的想要一个新的子,我只需设置一个触发器:
use strict;
use warnings;
sub map_even(&@) {
my $block = shift;
my $even = 1;
map {($even ^= 1) ? $block->() : $_} @_;
}
print join " ", map_even {$_*$_} 1,2,3,4;
所有输出:
1 4 3 16