Perl对readline
函数(以及等效的<>
I/O运算符)进行了一些特殊处理,它处理表达式
while (<HANDLE>)
while (readline(HANDLE))
Run Code Online (Sandbox Code Playgroud)
相当于
while (defined($_ = <HANDLE>))
Run Code Online (Sandbox Code Playgroud)
比照
$ perl -MO=Deparse -e 'f($_) while <>'
f($_) while defined($_ = <ARGV>); <--- implicitly sets $_
-e syntax OK
Run Code Online (Sandbox Code Playgroud)
但是如果你劫持这个readline
功能,这种自动分配似乎不会发生:
$ perl -MO=Deparse -e 'BEGIN {
> *CORE::GLOBAL::readline = sub { }
> }
> f($_) while <>'
sub BEGIN {
*CORE::GLOBAL::readline = sub {
};
}
f($_) while readline(ARGV); <--- doesn't set $_ !
-e syntax OK
Run Code Online (Sandbox Code Playgroud)
当然,这会使自定义readline
函数对许多遗留代码的工作不正确.这段代码的输出是"foo"
BEGIN块,"bar"
没有它,但我希望它是"BAR"
.
use warnings;
BEGIN { *CORE::GLOBAL::readline = \&uc_readline; }
sub uc_readline {
my $line = CORE::readline(shift || *ARGV);
return uc $line if defined $line;
return;
}
($_, $bar) = ("foo\n", "bar\n");
open X, '<', \$bar;
while (<X>) {
print $_; # want and expect to see "BAR\n"
}
Run Code Online (Sandbox Code Playgroud)
我有什么选择来劫持这个readline
功能但仍能得到正确的while (<...>)
成语处理?while (defined($_=<...>))
在所有遗留代码中显式转换所有内容是不切实际的.
这是一个相当肮脏的黑客使用重载来检测布尔上下文,但它似乎做了伎俩.在生产环境中使用此解决方案之前,它确实需要比我给出的更多测试:
use warnings;
BEGIN { *CORE::GLOBAL::readline = \&uc_readline; }
sub uc_readline {
my $line = CORE::readline(shift || *ARGV);
return Readline->new(uc $line) if defined $line;
return;
}
{package Readline;
sub new {shift; bless [@_]}
use overload fallback => 1,
'bool' => sub {defined($_ = $_[0][0])}, # set $_ in bool context
'""' => sub {$_[0][0]},
'+0' => sub {$_[0][0]};
}
my $bar;
($_, $bar) = ("foo\n", "bar\n");
open X, '<', \$bar;
while (<X>) {
print $_; # want and expect to see "BAR\n"
}
Run Code Online (Sandbox Code Playgroud)
打印:
BAR
Run Code Online (Sandbox Code Playgroud)
这也将使if (<X>) {...}
集合$_
.我不知道是否有办法将魔法限制为仅while
循环.