Perlのフリップフロップ演算子は盗聴されていますか?グローバル状態がありますが、どうすればリセットできますか?
-
23-09-2019 - |
質問
私はがっかりしています。 さて、これはおそらく最もだった 楽しい Perl 私が今まで見つけたバグ。今日でも私はPerlについて新しいことを学んでいます。基本的に、フリップフロップ演算子は ..
これは返します 違うんだ。 左側が戻るまで 真, 、そして 真 右側が戻るまで 違うんだ。 グローバル状態を維持する(またはそれが私が想定していることです。)
私はそれをリセットすることはできますか(おそらくこれはほとんど使われていないPerl4風の良い追加になるでしょう 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
呼び出してリセットすることができます(ボイドコンテキストでも) keys
ハッシュに。しかし、 glob
また ..
, 、リセットがリセットされるまで呼び出すことを除いて、リセットメカニズムはありません。サンプルグローブバグ:
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";
私はこれについても書いています 排他的なフリップフロップ演算子を作る.
はい、アイテムレベルのごとにアクセス許可を提供できます。Wikiページのライブラリの表示中にWikiページ項目のドロップダウンメニューを選択するだけで、アクセス許可を調整できます。
特定のケースの回避策/ハック/チートは、アレイに最終値を追加することです。
sub search {
my $arr = shift;
grep { !( /start/ .. /never_exist/ ) } @$arr, 'never_exist';
}
これにより、RHS of Rangeオペレーターが最終的に真実になることが保証されます。
もちろん、これは決して一般的な解決策ではありません。
私の意見では、この行動は明確に文書化されていません。明確な説明を作成できれば、パッチを適用できます perlop.pod
経由 perlbug
.
私はこの問題を発見しました、そして私が知る限り、それを修正する方法はありません。アップショットは - 使用しないでください ..
関数の演算子は、関数を離れるときに偽の状態を残していると確信している場合を除き、関数は同じ入力に対して異なる出力を返すことがあります(または同じ入力に対して異なる動作を示します)。
の各使用 ..
オペレーターは独自の状態を維持しています。アレックス・ブラウンが言ったように、あなたは関数を離れるときにそれを偽の状態に残す必要があります。多分あなたは次のようなことをすることができます:
sub search {
my $arr = shift;
grep { !( /start/ || $_ eq "my magic reset string" ..
/never_exist/ || $_ eq "my magic reset string" ) }
(@$arr, "my magic reset string");
}