繁体   English   中英

Perl的触发器操作员是否被窃听? 它具有全局状态,我该如何重置它?

[英]Is Perl's flip-flop operator bugged? It has global state, how can I reset it?

我很沮丧。 好的,所以这可能是我见过的最有趣的 Perl bug。 即使在今天,我也在学习关于Perl的新内容。 从本质上讲,触发器运营商..直到左手侧返回true,然后 ,直到右手边返回false保持全局的状态,这返回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

有人可以澄清文档的问题是什么吗? 它清楚地表明:

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

关于“每个”的含义有一些含糊不清的含义,但我不认为复杂的解释可以很好地满足文档的要求。

请注意,Perl的其他迭代器( each或标量上下文glob )可能会导致相同的问题。 因为each状态each绑定到特定的散列,而不是特定的代码位,所以each都可以通过调用哈希上的(甚至在void上下文中) keys来重置。 但对于glob.. ,除了通过调用迭代器直到它被重置之外,没有可用的重置机制。 一个示例glob bug:

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

我还在Make独有的触发器操作器中写了这个。

“范围运算符” ..在记录perlop中下“范围操作符”。 通过doucmentation看,似乎没有任何方法可以重置..运算符的状态。 ..运算符的每个实例都保持自己的状态,这意味着没有任何方法可以引用任何特定的..运算符的状态。

它看起来像是为非常小的脚本设计的,例如:

if (101 .. 200) { print; }

文档说明这是简称

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

不知何故使用$. 隐含在那里(工具在评论中指出,也记录了这一点)。 这个想法似乎是这个循环运行一次 (至$. == 200 )在Perl解释器的给定实例,因此您不必担心复位的状态..触发器。

由于您已确定的原因,此运算符在更通用的可重用上下文中似乎不太有用。

针对您的特定情况的解决方法/黑客/作弊是将最终值附加到您的数组:

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

这将保证范围运营商的RHS最终成立。

当然,这绝不是一般解决方案。

在我看来,这种行为没有明确记录。 如果你可以构建一个清晰的解释,你可以申请一个补丁perlop.pod通过perlbug

我发现了这个问题,据我所知,没有办法解决它。 结果是 - 不要在函数中使用..运算符,除非您确定在离开函数时将其保留为false状态,否则函数可能会为同一输入返回不同的输出(或者表现出不同的行为)相同的输入)。

每个使用..运算符都保持自己的状态。 就像Alex Brown说的那样,当你离开这个函数时,你需要让它处于假状态。 也许你可以这样做:

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 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM