我很沮丧。好的,所以这可能是我发现的最强有趣 Perl错误。即使在今天,我也在学习关于Perl的新内容。本质上,触发器操作符..
返回 false ,直到左侧返回 true ,然后 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
答案 0 :(得分:34)
有人可以澄清文档的问题是什么吗?它清楚地表明:
Each ".." operator maintains its own boolean state.
关于“每个”的含义有一些含糊不清的含义,但我不认为复杂的解释可以很好地提供文档。
请注意,Perl的其他迭代器(each
或标量上下文glob
)可能会导致相同的问题。因为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
每个递归深度都是一个单独的..运算符。
答案 1 :(得分:18)
诀窍不是使用相同的flip-flop,所以你没有任何状态可以担心。只需创建一个生成器函数,为您提供一个新的子程序,只需使用一次新的触发器:
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 exclusive flip-flop operators写了这篇文章。
答案 2 :(得分:7)
“范围运算符”..
记录在“范围运算符”下的perlop中。通过显示,似乎没有任何方法可以重置..
运算符的状态。 ..
运算符的每个实例都保持其自己的状态,这意味着没有任何方法可以引用任何特定..
运算符的状态。
它看起来像是为非常小的脚本设计的,例如:
if (101 .. 200) { print; }
文档说明这是
的缩写if ($. == 101 .. $. == 200) { print; }
不知何故,$.
的使用隐含在那里(工具在评论中指出也是如此)。这个想法似乎是这个循环在Perl解释器的给定实例中运行一次(直到$. == 200
),因此您不必担心重置{的状态{1}}触发器。
由于您确定的原因,此运算符在更通用的可重用上下文中似乎不太有用。
答案 3 :(得分:7)
针对您的特定情况的解决方法/黑客/作弊是将最终值附加到您的数组:
sub search {
my $arr = shift;
grep { !( /start/ .. /never_exist/ ) } @$arr, 'never_exist';
}
这将保证范围运算符的RHS最终成立。
当然,这绝不是一般解决方案。
在我看来,这种行为没有明确记录。如果您可以构建明确的解释,则可以通过perlop.pod
将修补程序应用于perlbug
。
答案 4 :(得分:2)
我发现了这个问题,据我所知,没有办法解决它。结果是 - 不要在函数中使用..
运算符,除非您确定在离开函数时将其保留为false状态,否则函数可能会返回相同输入的不同输出(或展示同一输入的不同行为。)
答案 5 :(得分:1)
..
运算符的每次使用都保持自己的状态。就像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");
}