如何使用模拟的"readline"函数自动分配到"$ _"?

mob*_*mob 11 perl

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($_=<...>))在所有遗留代码中显式转换所有内容是不切实际的.

Eri*_*rom 6

这是一个相当肮脏的黑客使用重载来检测布尔上下文,但它似乎做了伎俩.在生产环境中使用此解决方案之前,它确实需要比我给出的更多测试:

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循环.