Есть ли ошибка в операторе триггера Perl?У него глобальное состояние, как его сбросить?

StackOverflow https://stackoverflow.com/questions/2143554

Вопрос

Я встревожен. ОК, это, наверное, было самое веселье Перл ошибка, которую я когда-либо находил.Даже сегодня я узнаю что-то новое о Perl.По сути, оператор триггера .. который возвращает ЛОЖЬ пока левая сторона не вернется истинный, а потом истинный пока правая часть не вернется ЛОЖЬ сохранить глобальное состояние (или я так предполагаю.)

Могу ли я его сбросить (возможно, это было бы хорошим дополнением к Perl 4, который почти никогда не используется) 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 оператора диапазона в конечном итоге будет правдой.

Конечно, это никоим образом не является общим решением.

На мой взгляд, это поведение не четко документировано. Если вы можете построить четкое объяснение, вы можете применить патч на perlop.pod с помощью perlbug.

Я нашел эту проблему, и, насколько я знаю, нет возможности ее исправить. Opphot - не используйте .. Оператор по функциям, если вы не уверены, что оставляете его в ложном состоянии, когда вы покидаете функцию, в противном случае функция может возвращать различные результаты для одного и того же ввода (или демонстрировать различное поведение для одного и того же ввода).

Каждое использование .. Оператор поддерживает собственное состояние. Как сказал Алекс Браун, вам нужно оставить его в ложном состоянии, когда вы оставляете функцию. Может, вы могли бы сделать что -то вроде:

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 с атрибуция
Не связан с StackOverflow
scroll top