Как я могу получить автоматическое присвоение $ _ с помощью фиктивной функции readline? - PullRequest
11 голосов
/ 22 февраля 2011

Perl имеет специальную обработку для функции readline (и эквивалентного оператора <> I / O), где он обрабатывает выражения

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

как эквивалент

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

ср

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

Но этого автоматического назначения, похоже, не произойдет, если вы перехватите функцию 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

Конечно, это приведет к некорректной работе пользовательской функции 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"
}

Какие варианты у меня есть, чтобы захватить функцию readline, но все равно получить правильное лечение идиомы while (<...>)? Непрактично явно конвертировать все в while (defined($_=<...>)) во всем унаследованном коде.

Ответы [ 2 ]

6 голосов
/ 22 февраля 2011

Это довольно грязный хак, использующий перегрузку для обнаружения логического контекста, но, похоже, он добился цели. Это, безусловно, требует больше тестирования, чем я дал, прежде чем использовать это решение в рабочей среде:

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 (<X>) {...} set $_. Я не знаю, есть ли способ ограничить магию только while петлями.

0 голосов
/ 23 февраля 2011

Этот код:

use warnings;
BEGIN { *CORE::GLOBAL::readline = \&uc_readline; }
sub uc_readline {
    my $line = CORE::readline(shift || *ARGV);
    return unless defined $line;
    $line = uc $line;
    $_ = $line;
    return $line;
}
($_, $bar) = ("foo\n", "bar\n");
open X, '<', \$bar;
while (<X>) {
  print $_;           # want and expect to see  "BAR\n"
}
print "$_";           # prints "BAR" instad of "foo"

делает почти правильные вещи, но $ _ не локализуется, поэтому после цикла $ _ устанавливается на последнее значение, считанное из дескриптора файла.Добавление Scope :: Upper к миксу исправляет следующее:

use warnings;
use Scope::Upper qw/localize SCOPE/;
BEGIN { *CORE::GLOBAL::readline = \&uc_readline; }
sub uc_readline {
    my $line = CORE::readline(shift || *ARGV);
    return unless defined $line;
    $line = uc $line;
    local $_ = $line;
    # localize $_ in the scope of the while
    localize *main::_, \$line, SCOPE(1);
    return $line;
}
($_, $bar) = ("foo\n", "bar\n");
open X, '<', \$bar;
while (<X>) {
  print "$_";           # want and expect to see  "BAR\n"
}
print "$_";             # will print 'foo', not "BAR"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...