Является ли Perl флип-флоп оператором? Он имеет глобальное состояние, как я могу reset его?
Я с тревогой. Хорошо, так что это было, вероятно, самое fun Perl ошибка, которую я когда-либо обнаружил. Даже сегодня я изучаю новый материал о Perl. По сути, оператор триггера ..
, который возвращает false, пока левая сторона не возвращает true, а затем true, пока правая сторона не возвращает false, сохраняет глобальное состояние (или это то, что я предполагаю).
Могу ли я reset его (возможно, это было бы хорошим дополнением к 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
Ответы
Ответ 1
Может кто-нибудь уточнить, что такое проблема с документацией? Он четко указывает:
Each ".." operator maintains its own boolean state.
Существует некоторая неопределенность в отношении того, что означает "каждый", но я не думаю, что документация будет хорошо обслуживаться сложным объяснением.
Обратите внимание, что другие итераторы Perl (each
или скалярный контекст glob
) могут приводить к тем же проблемам. Поскольку состояние для each
привязано к определенному хэшу, а не к определенному биту кода, each
может быть reset, вызывая (даже в контексте void) keys
в хэше. Но для glob
или ..
механизм reset недоступен, кроме как путем вызова итератора, пока он не будет reset. Пример 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 .. 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
Каждая глубина рекурсии является отдельным оператором.
Ответ 2
Трюк не использует тот же флип-флоп, поэтому вам не о чем беспокоиться. Просто создайте функцию генератора, чтобы дать вам новую подпрограмму с новым триггером, который вы используете только один раз:
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";
Я также пишу об этом в Сделать эксклюзивные флип-флоп-операторы.
Ответ 3
"Оператор диапазона" ..
описан в perlop в разделе "Операторы диапазона". Рассматривая процесс погашения, кажется, что нет reset состояния оператора ..
. Каждый экземпляр оператора ..
сохраняет свое собственное состояние, а это означает, что нет никакого способа ссылаться на состояние какого-либо конкретного оператора ..
.
Похоже, он разработан для очень маленьких скриптов, таких как:
if (101 .. 200) { print; }
В документации указано, что это сокращение для
if ($. == 101 .. $. == 200) { print; }
Как-то использование $.
там неявно (инструмент указывает в комментарии, который также задокументирован). Идея состоит в том, что этот цикл работает один раз (до $. == 200
) в данном экземпляре интерпретатора Perl, и поэтому вам не нужно беспокоиться о сбросе состояния флип-флопа ..
.
Этот оператор не кажется слишком полезным в более общем контексте многократного использования по причинам, которые вы определили.
Ответ 4
Обходной путь /hack/cheat для вашего конкретного случая - добавить конечное значение к вашему массиву:
sub search {
my $arr = shift;
grep { !( /start/ .. /never_exist/ ) } @$arr, 'never_exist';
}
Это гарантирует, что RHS оператора диапазона в конечном итоге будет правдой.
Конечно, это никоим образом не является общим решением.
По-моему, это поведение явно не задокументировано. Если вы можете построить четкое объяснение, вы можете применить патч к perlop.pod
через perlbug
.
Ответ 5
Я нашел эту проблему, и, насколько я знаю, нет возможности ее исправить. Результатом является: не используйте оператор ..
в функциях, если вы не уверены, что оставите его в ложном состоянии, когда вы оставите функцию, иначе функция может возвращать разные выходные данные для одного и того же ввода (или проявлять другое поведение для того же входа).
Ответ 6
Каждое использование оператора ..
сохраняет свое собственное состояние. Как сказал Алекс Браун, вам нужно оставить его в ложном состоянии, когда вы оставите функцию. Возможно, вы могли бы сделать что-то вроде:
sub search {
my $arr = shift;
grep { !( /start/ || $_ eq "my magic reset string" ..
/never_exist/ || $_ eq "my magic reset string" ) }
(@$arr, "my magic reset string");
}