Подтвердить что ты не робот

Является ли 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
4b9b3361

Ответ 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");
}