其他答案错过的非常重要的部分是这条线
grep {s/(\d+)/sprintf"%06.6d",$1/ge,1} @_;
实际上是在修改传递给函数的参数,而不是它们的副本。
grep
是一个过滤命令,$_
代码块中的值是 中的值之一的别名@_
。 @_
反过来包含传递给函数的参数的别名,因此当s///
运算符执行其替换时,将对原始参数进行更改。这在以下示例中显示:
sub test {grep {s/a/b/g; 1} @_}
my @array = qw(cat bat sat);
my @new = test @array;
say "@new"; # prints "cbt bbt sbt" as it should
say "@array"; # prints "cbt bbt sbt" as well, which is probably an error
您正在寻找的行为(应用修改$_
列表副本的函数)已被封装为apply
多个模块中的函数。我的模块List::Gen包含这样的实现。 apply
自己写也相当简单:
sub apply (&@) {
my ($sub, @ret) = @_;
$sub->() for @ret;
wantarray ? @ret : pop @ret
}
这样,您的代码可以重写为:
sub natural_sort {
apply {s/(^|\D)0+(\d)/$1$2/g} sort apply {s/(\d+)/sprintf"%06.6d",$1/ge} @_
}
如果您使用重复替换的目标是执行一种应用了瞬态修改的原始数据,您应该研究一种称为Schwartzian 变换的 Perl 习语,这是实现该目标的更有效方法。