Триггерный оператор Perl прослушивается? Это имеет глобальное состояние, как я могу сбросить его?

Я встревожен. Хорошо, таким образом, это было, вероятно, самой забавной ошибкой Perl, я когда-либо находил. Даже сегодня я узнаю новый материал о Perl. По существу, триггерный оператор .. который возвращает false, пока левая сторона не возвращает true, и затем верный, пока правая сторона не возвращает 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

29
задан Peter Mortensen 21 January 2012 в 13:30
поделиться

6 ответов

Кто-нибудь может прояснить, в чем проблема с документацией? Это ясно указывает:

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

Там есть некоторая неясность в том, что означает "каждый", но я не думаю, что документация будет хорошо подкреплена сложным объяснением.

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

Threads:

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

Threaded code начинается с того, какое состояние ... было до создания потока, но изменения его состояния в потоке изолированы от воздействия на что-либо другое.

Рекурсия:

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

Каждая глубина рекурсии является отдельным ... оператором.

34
ответ дан 28 November 2019 в 01:09
поделиться

Хитрость в том, что вы не используете тот же флип-флоп , так что вам не о чем беспокоиться. Просто сделайте функцию генератора, чтобы дать вам новую подпрограмму с новым флип-флопом, который вы используете только один раз:

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";

Я также пишу об этом в Сделайте эксклюзивные операторы флип-флоп .

18
ответ дан 28 November 2019 в 01:09
поделиться

Обходной путь / Hack / Cheat для вашего конкретного случая - добавить конечное значение в ваш массив:

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

Это гарантирует, что оператор RHS of Range будет в конечном итоге.

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

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

7
ответ дан 28 November 2019 в 01:09
поделиться

Я нашел эту проблему, и, насколько я знаю, нет способа его исправления. Upshot - не используйте оператор .. .. .. . (или проявлять разное поведение для того же ввода).

2
ответ дан 28 November 2019 в 01:09
поделиться

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

sub search {
  my $arr = shift;
  grep { !( /start/ || $_ eq "my magic reset string" ..
            /never_exist/ || $_ eq "my magic reset string" ) } 
      (@$arr, "my magic reset string");
}
1
ответ дан 28 November 2019 в 01:09
поделиться

«Оператор диапазона» .. Документировано в Perlop под «операторами диапазона». Глядя через увлечение, кажется, что нет никакого способа сбросить состояние .. Оператор. Каждый экземпляр .. . Оператор поддерживает его собственное состояние , что означает, что нет никакого способа ссылаться на состояние какого-либо конкретного .. .

Похоже, он предназначен для очень маленьких сценариев, таких как:

if (101 .. 200) { print; }

Документация гласит, что это коротко для

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

как-то использования $. там неявно (на музыкальных указываются в комментарии о том, что это тоже документировано). Идея, кажется, в том, что эта петля работает один раз (до $. == 200 ) в данном экземпляре интерпретатора Perl, и поэтому вам не нужно беспокоиться о сбросе Состояние .. Флип-флоп.

Этот оператор не слишком полезен в более общем многоразовом контексте, по причинам, которые вы определили.

7
ответ дан 28 November 2019 в 01:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: