2011-02-22 16 views
11

Perl,

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

karş ifadeleri eşdeğer olarak

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

davranır readline fonksiyonu için bazı özel işleme (ve buna denk <> I/O operatörü) sahip olan
$ perl -MO=Deparse -e 'f($_) while <>' 
f($_) while defined($_ = <ARGV>);  <--- implicitly sets $_ 
-e syntax OK 

Ama readline fonksiyonunu kaçırmak eğer bu otomatik atama gerçekleşmesi görünmüyor:

$ 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 

Tabii ki, bu eski kod bir sürü için yanlış özel readline fonksiyon çalışmalarını yapacaktır. Bu kod BEGIN bloğu ve "bar" olmadan bu kod "foo" ise, ancak "BAR" olmasını istiyorum.

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

seçeneklerim nelerdir readline fonksiyonunu kaçırmak ama yine while (<...>) deyim uygun tedavi almak gerekiyor? Tüm eski kodlarda her şeyi açıkça while (defined($_=<...>))'a dönüştürmek pratik değildir.

+3

burada neden ('Perl_newLOOPOP', opmini.c: 5318) bu dönüşümü yapan kod şudur: karışıma Scope::Upper eklemek, o düzeltir optree çalışır ve bir' OP_READLINE' arıyor - ancak eğer "CORE :: GLOBAL :: readline" tanımlanırsa, derleme yerine "OP_ENTERSUB" yerine kendi yerini alacak, böylece while-dönüşüm asla gerçekleşmeyecektir. Bu bir hata olarak nitelendirir ya da değil emin değilim :) – hobbs

+0

Ugh, korkarım ki böyle bir şeydi. Bir hata değilse, en azından belgelenmemiş. – mob

+0

oops, sanki seni perlbug'a gönderdiğimi söylemek için çok bekledim gibi görünüyor - çünkü sende öyle yaptın. Tamam, bir test vakası verdiniz :) – hobbs

cevap

6

Bu, boole içeriğini algılamak için aşırı yüklemeyi kullanan oldukça kirli bir saldırıdır, ancak hile yapmak gibi görünüyor.

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

yazdırır:

BAR 

Bu aynı zamanda if (<X>) {...}$_ set yapacak Kesinlikle ben bir üretim ortamında bu çözümü kullanmadan önce onu verdik daha test ihtiyacı var. Büyüyü sadece while döngülerle sınırlamanın bir yolu olup olmadığını bilmiyorum.

+0

Eh, bir şey için, boolean bağlamında 'tanımlı 'testini çıkardınız. Ama düzeltmesi kolay. – cjm

+0

@cjm => Ne demek istiyorsun?'uc_readline' zaten zaten aşırı yüklenme olmadan bir' tanımlı 'testi içerir –

+0

Eğer '$ line' bir tanımlı-ama-false değeri (' '0' 'gibi) içeriyorsa, onu' $ _ 'olarak atar ve geri döndürürsünüz. Bu 'while' döngüsünü sonlandıracak. Boole bağlamında '$ _ 'yerine' tanımlı $ _' döndürmelisiniz. – cjm

0

Bu kod:

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" 

neredeyse doğru olanı yapar, ancak döngü sonra, $ _ dosya tanıtıcısından okunan son değere ayarlanır böylece _, lokalize değildir $.

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" 
+0

Ancak bu, "$ _" ($ line = ) 'derken de" $ "değerini ayarlar. – mob

İlgili konular