Perl 5.10+ 中词法 $_ 的优缺点

Posted

技术标签:

【中文标题】Perl 5.10+ 中词法 $_ 的优缺点【英文标题】:The good, the bad, and the ugly of lexical $_ in Perl 5.10+ 【发布时间】:2011-03-24 22:12:24 【问题描述】:

从 Perl 5.10 开始,现在可以对上下文变量 $_ 进行词法作用域,或者显式为 my $_; 或在 given / when 构造中。

有没有人发现 $_ 的好用法?它是否使任何构造更简单/更安全/更快?

如果情况变得更复杂怎么办?词汇$_ 是否在您的代码中引入了任何错误? (因为写入$_ 的控制结构将使用词法版本,如果它在范围内,如果它包含任何子例程调用(由于失去动态范围),这可以改变代码的行为)

最后,我想构建一个列表,阐明何时将$_ 用作词法、全局或什么时候根本不重要。


注意:截至perl5-5.24,这些实验性功能为no longer part of perl。

【问题讨论】:

我认为你的问题的答案大部分都包含在我的问题的答案中here。 @xenoterracide => 那里的答案肯定涵盖了词汇$_ 的某些方面,但不是全部。这就是我想在这里介绍的内容。 我知道,但最好有一个开始的好地方;) 好问题。我会+1,但我的选票用完了。 :) 【参考方案1】:

IMO,词法 $_ 带来的一件好事是新的 _ 原型符号。

这允许您指定一个子例程,以便它采用一个标量,或者如果没有提供,它将获取$_

所以不要写:

sub foo 
    my $arg = @_ ? shift : $_;

    # Do stuff with $_

我会写:

sub foo(_) 
    my $arg = shift;

    # Do stuff with $_ or first arg.

变化不大,但当我想要这种行为时,它就简单多了。去除样板是一件好事。

当然,这会产生改变几个内置函数原型的连锁反应(例如chr),这可能会破坏一些代码。

总的来说,我欢迎词法$_。它给了我一个工具,我可以用它来限制意外的数据修改和函数之间的奇怪交互。如果我决定在函数体中使用$_,通过词法化它,我可以确定无论我调用什么代码,$_ 在调用代码中都不会被修改。

动态范围很有趣,但在大多数情况下,我想要词法范围。再加上$_ 周围的复杂情况。我听说过关于简单地做local $_; 不明智的可怕警告——最好改用for ( $foo ) 。当我以任何方式本地化$_ 时,词汇化的$_ 给了我100 次中的99 次我想要的东西。词法$_ 使一个非常方便和可读性更强大的功能。

我的大部分工作都必须使用 perl 5.8,因此我没有在大型项目中使用词汇 $_ 的乐趣。但是,感觉这将大大有助于使$_ 的使用更安全,这是一件好事。

【讨论】:

给出了for ($foo) ...而不是local *_ = \$foo;的原因是什么?根据我的经验,后者要快一些(因为它不必为单个元素列表设置范围和迭代器),但如果有任何安全问题需要牢记,我会很感兴趣。 Eric,我今天去找了具体的参考,没找到。在这一点上,我所能记得的只是对local $_ 的警告,该警告来自 2009 年年中某个时候来自“我尊重其意见的人”。因此,除非我能更好地证实这一说法,否则最好将其视为道听途说。声称是本地化并没有涵盖具有公认特殊$_ 的所有边缘情况。我从来没有找到错误报告或测试用例来支持警告。【参考方案2】:

我曾经在玩Inline 模块时发现了一个issue(这个词太强了)。这个简单的脚本:

use strict qw(vars subs);
for ('function') 
    $_->();

sub function 
  require Inline;
  Inline->bind(C => <<'__CODE__');
void foo() 


__CODE__

失败并显示Modification of a read-only value attempted at /usr/lib/perl5/site_perl/5.10/Inline/C.pm line 380. 错误消息。在Inline 模块的内部深处是一个想要修改$_ 的子程序,导致上面的错误消息。

使用

for my $_ ('function')  ...

或以其他方式声明 my $_ 是解决此问题的可行解决方法。

Inline 模块已修补以修复此特定问题)。

【讨论】:

【参考方案3】:

[ 基本原理: 一个简短的附加答案,为可能路过的 perl 新手提供快速总结。当搜索“perl lexical topic”时,可以在这里结束。]

到现在(2015 年),我想众所周知,词汇主题(my $_ 和一些相关功能)的引入会在一开始就导致一些难以检测的意外行为,marked 和 experimental 也是如此然后输入了deprecation stage。


部分摘要 #RT119315: 一个建议是use feature 'lextopic';to make use of a new lexical topic variable: $^_。 另一点是“implicit name for the topicalizing operator ... other than $_”在与明确的词法函数(例如词法maplmap)结合使用时效果最好。这些方法是否能以某种方式挽救given/when 尚不清楚。在实验和折旧阶段的来世,也许某些东西最终可能会继续存在于 CPAN 的河流中。

【讨论】:

【参考方案4】:

在这里没有遇到任何问题,尽管在谈到 Perls 魔法时,我倾向于遵循某种“不问,不说”的政策。 IE。例程通常不希望依赖于他们的同行将非词汇数据作为副作用,也不会让他们。

我已经针对各种 5.8 和 5.10 版本的 perl 测试了代码,同时使用 5.6 描述 Camel 作为偶尔参考。没有遇到任何问题。我的大部分东西最初都是为 perl 5.8.8 完成的。

【讨论】:

以上是关于Perl 5.10+ 中词法 $_ 的优缺点的主要内容,如果未能解决你的问题,请参考以下文章

perl 5.8 和 5.10 之间的区别 [关闭]

[Perl] 在 perl 5.10 中读取带分隔符的文本文件并插入到 mysql 表中

在linux下的如何将perl默认版本5.8.8升级为5.10

高效 pre-perl-5.10 等效于 pack("Q>")

Perl 5.10 是不是把原型弄乱了?

我可以确保在 5.10+ 上编写的 Perl 代码可以在 5.8 上运行吗?