【问题标题】:In Perl, why does the `while(<HANDLE>) {...}` construct not localize `$_`?在 Perl 中,为什么 `while(<HANDLE>) {...}` 构造不能本地化 `$_`?
【发布时间】:2011-07-27 04:24:33
【问题描述】:

Perl 没有使用以下语法自动本地化 $_ 的设计(或技术)原因是什么:

while (<HANDLE>) {...}

改写为:

while (defined( $_ = <HANDLE> )) {...}

隐式写入$_ 的所有其他构造都以本地化方式(for/foreachmapgrep)执行此操作,但使用while,您必须显式本地化变量:

local $_;
while (<HANDLE>) {...}

我的猜测是它与在“Super-AWK”模式下使用 Perl 和命令行开关有关,但这可能是错误的。

如果有人知道(或者更好的是参与了语言设计讨论),您能否与我们分享这种行为背后的原因?更具体地说,为什么允许 $_ 的值在被认为很重要的循环之外持续存在,尽管它可能导致错误(我倾向于在 SO 和其他 Perl 代码中到处看到)?


如果从上面看不清楚,$_必须本地化为while的原因如下例所示:

sub read_handle {
    while (<HANDLE>) { ... }
}

for (1 .. 10) {
     print "$_: \n"; # works, prints a number from 1 .. 10
     read_handle;
     print "done with $_\n";  # does not work, prints the last line read from
                              # HANDLE or undef if the file was finished
}

【问题讨论】:

  • 这就是为什么我(几乎)从不信任 $_。
  • 我想你的意思是 { local $_;而 () { ... } }
  • @MkV => 上面只是一个代码 sn-p(因此是 ...,没有定义 HANDLE 等)。周围的范围是隐含的(无论是裸块、子程序块、控制语句块......)如果你因此而否决了这个问题,那真是可悲。
  • 具有所需行为的解决方法:for (local $_=&lt;H&gt;;defined;$_=&lt;H&gt;) ...
  • 更多细节:while (defined (local $_ = &lt;HANDLE&gt;)) { ... }

标签: perl language-design while-loop local


【解决方案1】:

来自 perlmonks.org 上的thread

foreach 有区别 和while 因为他们完全是两个 不同的东西。 foreach 总是 循环时分配给变量 在一个列表上,而while 通常 没有。只是while (&lt;&gt;)是 一个例外,只有当有 单钻经营者有一个 隐式赋值给$_

还有:

while(&lt;&gt;) 的一个可能原因 不会将$_ 隐式本地化为 它的部分魔力在于有时 你想访问的最后一个值 $_ 在循环外。

【讨论】:

  • And also,这就是这个问题的症结所在,为什么这种行为被认为足够重要以证明它可能导致的错误是合理的。就我个人而言,我从来不需要 $_ 的值来持续超出循环,但有时会因为忘记 local $_ 而被咬伤。
【解决方案2】:

很简单,while 永远不会本地化。没有变量与 while 构造相关联,因此它甚至没有任何需要本地化的内容。

如果您在while 循环表达式或while 循环体中更改了某些变量,则您有责任对其进行充分限定。

【讨论】:

  • 没有变量与while 构造相关联,但肯定有一个变量与while (&lt;HANDLE&gt;) 构造相关联。我认为这就像调整重写while (&lt;HANDLE&gt;) 以生成{local $_; while (defined ($_ = &lt;HANDLE&gt;)) {...}} 的窥视孔优化器一样简单
  • @Eric Strom,我想不出该解决方案有任何问题——它不会破坏范围界定、下一个/最后一个/重做或异常——但我认为它需要一些重大更改允许优化器区分while (&lt;$fh&gt;)(应该本地化)和while (defined($_ = &lt;$fh&gt;))(不应该)。
【解决方案3】:

推测:因为forforeach 是迭代器和循环值,而while 在条件上运行。在while (&lt;FH&gt;) 的情况下,条件是从文件中读取了数据。 &lt;FH&gt; 是写给$_ 的,而不是while。隐含的defined() 测试只是防止幼稚代码在读取错误值时终止循环的一种方式。

对于其他形式的while 循环,例如while (/foo/)不会想要本地化 $_

虽然我同意 while (&lt;FH&gt;) 本地化 $_ 会很好,但它必须是一个非常特殊的情况,这可能会导致识别何时触发以及何时不触发的其他问题,就像规则一样对于&lt;EXPR&gt;,区分是句柄读取还是对glob的调用。

【讨论】:

    【解决方案4】:

    作为旁注,我们只写while(&lt;$fh&gt;) 因为Perl 没有真正的迭代器。如果 Perl 有适当的迭代器,&lt;$fh&gt; 将返回一个。 for 将使用它一次迭代一行,而不是将整个文件放入数组中。不需要while(&lt;$fh&gt;) 或与之相关的特殊情况。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-07-08
      • 1970-01-01
      • 2019-02-23
      • 1970-01-01
      • 2018-11-14
      • 2016-02-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多