这段 perl 代码中 $_ 被修改在哪里?
以下 perl 代码在 PerlCritic 中生成警告(由 Activestate):
sub natural_sort {
my @sorted;
@sorted = grep {s/(^|\D)0+(\d)/$1$2/g,1} sort grep {s/(\d+)/sprintf"%06.6d",$1/ge,1} @_;
}
生成的警告是:
不要修改列表函数中的$_
我不理解该警告,因为我不认为我正在修改 $_,尽管我想我必须这样做。 有人可以向我解释一下吗?
The following perl code generates a warning in PerlCritic (by Activestate):
sub natural_sort {
my @sorted;
@sorted = grep {s/(^|\D)0+(\d)/$1$2/g,1} sort grep {s/(\d+)/sprintf"%06.6d",$1/ge,1} @_;
}
The warning generated is:
Don't modify $_ in list functions
More info about that warning here
I don't understand the warning because I don't think I'm modifying $_, although I suppose I must be.
Can someone explain it to me please?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
您的两个
grep
都在修改$_
,因为您正在使用s//
。例如,这:与此相同:
我认为您最好使用
map
因为您没有使用grep
过滤任何内容,您只是使用grep
作为迭代器:这应该做同样的事情并且让批评者保持安静。您可能想看看
List::MoreUtils
如果您想要一些比普通map
更好的列表运算符。Both of your
grep
s are modifying$_
because you're usings//
. For example, this:is the same as this:
I think you'd be better off using
map
as you are not filtering anything with yourgrep
s, you're just usinggrep
as an iterator:That should do the same thing and keep critic quiet. You might want to have a look at
List::MoreUtils
if you want some nicer list operators than plainmap
.您正在 grep 中进行替换(即
s///
),这会修改$_
即正在 grep 的列表。You are doing a substitution ( i.e.
s///
) in the grep, which modifies$_
i.e. the list being grepped.这种情况和其他情况在 perldoc perlvar 中进行了解释:
This and other cases are explained in perldoc perlvar:
很多人都正确地回答了
s
运算符正在修改$_
,但是在即将发布的 Perl 5.14.0 中将会有新的r
s
运算符(即s///r
)的 code> 标志,它不会就地修改,而是返回修改后的元素。欲了解更多信息,请访问 有效的佩勒。您可以使用 perlbrew 来安装这个新版本。编辑:Perl 5.14 现已推出! 公告 公告 Delta
这是 mu 建议的函数(使用
map
),但使用此功能:Many people have correctly answered that the
s
operator is modifying$_
, however in the soon to be released Perl 5.14.0 there will be the newr
flag for thes
operator (i.e.s///r
) which rather than modify in-place will return the modified elements. Read more at The Effective Perler . You can use perlbrew to install this new version.Edit: Perl 5.14 is now available! Announcement Announcement Delta
Here is the function suggested by mu (using
map
) but using this functionality:其他答案忽略的非常重要的部分是,该行
实际上修改了传递给函数的参数,而不是它们的副本。
grep
是一个过滤命令,代码块内的$_
中的值是@_
中的值之一的别名。@_
又包含传递给函数的参数的别名,因此当s///
运算符执行其替换时,将对原始参数进行更改。这如以下示例所示:您正在寻找的行为(应用将
$_
修改为列表副本的函数)已被封装为apply
函数在许多模块中。我的模块 List::Gen 包含这样的实现。apply
自己编写也相当简单:这样,您的代码可以重写为:
如果重复替换的目标是应用瞬态修改来执行某种原始数据,那么您应该研究一个名为 Schwartzian 变换 的 Perl 习惯用法,这是实现该目标的更有效方法。
The VERY important part that other answers have missed is that the line
Is actually modifying the arguments passed into the function, and not copies of them.
grep
is a filtering command, and the value in$_
inside the code block is an alias to one of the values in@_
.@_
in turn contains aliases to the arguments passed to the function, so when thes///
operator performs its substitution, the change is being made to the original argument. This is shown in the following example:The behavior you are looking for (apply a function that modifies
$_
to a copy of a list) has been encapsulated as theapply
function in a number of modules. My module List::Gen contains such an implementation.apply
is also fairly simple to write yourself:With that, your code could be rewritten as:
If your goal with the repeated substitutions is to perform a sort of the original data with a transient modification applied, you should look into a Perl idiom known as the Schwartzian transform which is a more efficient way of achieving that goal.