我怎样才能使用模拟的“readline"函数自动分配给“$_"? [英] How can I still get automatic assignment to '$_' with a mocked 'readline' function?

查看:33
本文介绍了我怎样才能使用模拟的“readline"函数自动分配给“$_"?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Perl 对 readline 函数(以及等效的 <> I/O 运算符)有一些特殊的处理,它处理表达式

Perl has some special handling for the readline function (and the equivalent <> I/O operator) where it treats the expressions

while (<HANDLE>)
while (readline(HANDLE))

相当于

while (defined($_ = <HANDLE>))

参见

$ perl -MO=Deparse -e 'f($_) while <>'
f($_) while defined($_ = <ARGV>);      <--- implicitly sets $_
-e syntax OK

但是如果你劫持了readline函数,这个自动分配似乎不会发生:

But this automatic assignment doesn't seem to happen if you hijack the readline function:

$ 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

当然,这会导致自定义 readline 函数在许多遗留代码中无法正常工作.这段代码的输出是带有 BEGIN 块的 "foo" 和没有它的 "bar",但我希望它是 "BAR".

Of course, this will make the custom readline function work incorrectly for a lot of legacy code. The output of this code is "foo" with the BEGIN block and "bar" without it, but I want it to be "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"
}

我有什么选择可以劫持 readline 函数,但仍然可以正确处理 while (<...>) 习语?在所有遗留代码中将所有内容显式转换为 while (defined($_=<...>)) 是不切实际的.

What options do I have to hijack the readline function but still get the proper treatment of the while (<...>) idiom? It's not practical to explicitly convert everything to while (defined($_=<...>)) in all the legacy code.

推荐答案

这是一个相当肮脏的黑客,使用重载来检测布尔上下文,但它似乎可以解决问题.在生产环境中使用此解决方案之前,它当然需要比我提供的更多测试:

This is a fairly dirty hack using overloading to detect boolean context, but it seems to do the trick. It certainly needs more testing than I have given it before using this solution in a production environment:

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"
}

打印:

BAR

这也会使 if () {...} 设置 $_.我不知道是否有办法将魔法限制在 while 循环中.

This will also make if (<X>) {...} set $_. I don't know if there is a way to limit the magic to only while loops.

这篇关于我怎样才能使用模拟的“readline"函数自动分配给“$_"?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆