簡體   English   中英

Perl的觸發器操作員是否被竊聽? 它具有全局狀態,我該如何重置它?

[英]Is Perl's flip-flop operator bugged? It has global state, how can I reset it?

我很沮喪。 好的,所以這可能是我見過的最有趣的 Perl bug。 即使在今天,我也在學習關於Perl的新內容。 從本質上講,觸發器運營商..直到左手側返回true,然后 ,直到右手邊返回false保持全局的狀態,這返回false(或者說是我承擔。)

我可以重置它(也許這將是Perl 4-esque幾乎沒有使用reset() )的一個很好的補充? 或者,沒有辦法安全地使用此操作員?

我也沒有看到perldoc perlop任何地方記錄的這個(全局上下文位)這是一個錯誤嗎?

use feature ':5.10';
use strict;
use warnings;

sub search {
    my $arr = shift;
    grep { !( /start/ .. /never_exist/ ) } @$arr;
}

my @foo = qw/foo bar start baz end quz quz/;
my @bar = qw/foo bar start baz end quz quz/;

say 'first shot - foo';
say for search \@foo;

say 'second shot - bar';
say for search \@bar;

擾流板

$ perl test.pl
first shot
foo
bar
second shot

有人可以澄清文檔的問題是什么嗎? 它清楚地表明:

Each ".." operator maintains its own boolean state.

關於“每個”的含義有一些含糊不清的含義,但我不認為復雜的解釋可以很好地滿足文檔的要求。

請注意,Perl的其他迭代器( each或標量上下文glob )可能會導致相同的問題。 因為each狀態each綁定到特定的散列,而不是特定的代碼位,所以each都可以通過調用哈希上的(甚至在void上下文中) keys來重置。 但對於glob.. ,除了通過調用迭代器直到它被重置之外,沒有可用的重置機制。 一個示例glob bug:

sub globme {
    print "globbing $_[0]:\n";
    print "got: ".glob("{$_[0]}")."\n" for 1..2;
}
globme("a,b,c");
globme("d,e,f");
__END__
globbing a,b,c:
got: a
got: b
globbing d,e,f:
got: c
Use of uninitialized value in concatenation (.) or string at - line 3.
got: 

對於過於好奇,這里有一些例子,其中相同..在源中是一個不同的 ..運算符:

單獨關閉:

sub make_closure {
    my $x;
    return sub {
        $x if 0;  # Look, ma, I'm a closure
        scalar( $^O..!$^O ); # handy values of true..false that don't trigger ..'s implicit comparison to $.
    }
}
print make_closure()->(), make_closure()->();
__END__
11

注釋掉$x if 0行,看看非閉包有一個由所有“副本”共享的單個..操作,輸出為12

主題:

use threads;
sub coderef { sub { scalar( $^O..!$^O ) } }
coderef()->();
print threads->create( coderef() )->join(), threads->create( coderef() )->join();
__END__
22

線程代碼以線程創建之前的任何狀態開始,但是線程中對其狀態的更改被隔離而不會影響其他任何內容。

遞歸:

sub flopme {
    my $recurse = $_[0];
    flopme($recurse-1) if $recurse;
    print " "x$recurse, scalar( $^O..!$^O ), "\n";
    flopme($recurse-1) if $recurse;
}
flopme(2)
__END__
1
 1
2
  1
3
 2
4

每個遞歸深度都是一個單獨的..運算符。

訣竅不是使用相同的觸發器,所以你沒有擔心的狀態。 只需創建一個生成器函數,為您提供一個新的子程序,只需使用一次新的觸發器:

sub make_search {
    my( $left, $right ) = @_;
    sub {
        grep { !( /\Q$left\E/ .. /\Q$right\E/ ) } @{$_[0]};
        }
}

my $search_sub1 = make_search( 'start', 'never_existed' );
my $search_sub2 = make_search( 'start', 'never_existed' );


my @foo = qw/foo bar start baz end quz quz/;

my $count1 = $search_sub1->( \@foo );
my $count2 = $search_sub2->( \@foo );

print "count1 $count1 and count2 $count2\n";

我還在Make獨有的觸發器操作器中寫了這個。

“范圍運算符” ..在記錄perlop中下“范圍操作符”。 通過doucmentation看,似乎沒有任何方法可以重置..運算符的狀態。 ..運算符的每個實例都保持自己的狀態,這意味着沒有任何方法可以引用任何特定的..運算符的狀態。

它看起來像是為非常小的腳本設計的,例如:

if (101 .. 200) { print; }

文檔說明這是簡稱

if ($. == 101 .. $. == 200) { print; }

不知何故使用$. 隱含在那里(工具在評論中指出,也記錄了這一點)。 這個想法似乎是這個循環運行一次 (至$. == 200 )在Perl解釋器的給定實例,因此您不必擔心復位的狀態..觸發器。

由於您已確定的原因,此運算符在更通用的可重用上下文中似乎不太有用。

針對您的特定情況的解決方法/黑客/作弊是將最終值附加到您的數組:

sub search { 
  my $arr = shift;
  grep { !( /start/ .. /never_exist/ ) } @$arr, 'never_exist';
} 

這將保證范圍運營商的RHS最終成立。

當然,這絕不是一般解決方案。

在我看來,這種行為沒有明確記錄。 如果你可以構建一個清晰的解釋,你可以申請一個補丁perlop.pod通過perlbug

我發現了這個問題,據我所知,沒有辦法解決它。 結果是 - 不要在函數中使用..運算符,除非您確定在離開函數時將其保留為false狀態,否則函數可能會為同一輸入返回不同的輸出(或者表現出不同的行為)相同的輸入)。

每個使用..運算符都保持自己的狀態。 就像Alex Brown說的那樣,當你離開這個函數時,你需要讓它處於假狀態。 也許你可以這樣做:

sub search {
  my $arr = shift;
  grep { !( /start/ || $_ eq "my magic reset string" ..
            /never_exist/ || $_ eq "my magic reset string" ) } 
      (@$arr, "my magic reset string");
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM