Perl 的触发器运算符有问题吗?它具有全局状态,我如何重置它?

发布于 2024-08-19 03:39:27 字数 913 浏览 9 评论 0原文

我很沮丧。好吧,这可能是最有趣 Perl 我发现过的错误。即使今天我仍在学习有关 Perl 的新知识。本质上,触发器运算符 .. 返回 false,直到左侧返回 true,然后返回 true< /em> 直到右侧返回 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

I'm dismayed. OK, so this was probably the most fun Perl bug I've ever found. Even today I'm learning new stuff about Perl. Essentially, the flip-flop operator .. which returns false until the left-hand-side returns true, and then true until the right-hand-side returns false keep global state (or that is what I assume.)

Can I reset it (perhaps this would be a good addition to Perl 4-esque hardly ever used reset())? Or, is there no way to use this operator safely?

I also don't see this (the global context bit) documented anywhere in perldoc perlop is this a mistake?

Code

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;

Spoiler

$ perl test.pl
first shot
foo
bar
second shot

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

独留℉清风醉 2024-08-26 03:39:27

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

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

“每个”的含义有些模糊,但我认为复杂的解释不会很好地服务于文档。

请注意,Perl 的其他迭代器(each 或标量上下文 glob)可能会导致相同的问题。由于 each 的状态绑定到特定的哈希值,而不是特定的代码位,因此可以通过调用(即使在 void 上下文中)keys< 来重置 each /code> 上的哈希值。但对于 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

注释掉 < code>$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

每个递归深度都是一个单独的 .. 运算符。

Can someone clarify what the issue with the documentation is? It clearly indicates:

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

There is some vagueness there about what "Each" means, but I don't think the documentation would be well served by a complex explanation.

Note that Perl's other iterators (each or scalar context glob) can lead to the same problems. Because the state for each is bound to a particular hash, not a particular bit of code,each can be reset by calling (even in void context) keys on the hash. But for glob or .., there is no reset mechanism available except by calling the iterator until it is reset. A sample 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: 

For the overly curious, here are some examples where the same .. in the source is a different .. operator:

Separate closures:

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

Comment out the $x if 0 line to see that non-closures have a single .. operation shared by all "copies", with the output being 12.

Threads:

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

Threaded code starts with whatever the state of the .. had been before thread creation, but changes to its state in the thread are isolated from affecting anything else.

Recursion:

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

Each depth of recursion is a separate .. operator.

玩心态 2024-08-26 03:39:27

诀窍是不使用相同的 触发器 所以你没有状态担心。只需创建一个生成器函数即可为您提供一个带有新触发器的新子例程,您只需使用一次:

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

我也在 制作独占触发器运算符

The trick is not use the same flip-flop so you have no state to worry about. Just make a generator function to give you a new subroutine with a new flip-flop that you only use once:

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

I also write about this in Make exclusive flip-flop operators.

想挽留 2024-08-26 03:39:27

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

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

if (101 .. 200) { print; }

文档指出这是 的缩写 不知

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

怎的,其中隐含了 $. 的使用(toolic 在注释中指出这也已记录在案)。这个想法似乎是这个循环在 Perl 解释器的给定实例中运行一次(直到$.== 200),因此您无需担心关于重置 .. 触发器的状态。

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

The "range operator" .. is documented in perlop under "Range Operators". Looking through the doucmentation, it appears that there isn't any way to reset the state of the .. operator. Each instance of the .. operator keeps its own state, which means there isn't any way to refer to the state of any particular .. operator.

It looks like it's designed for very small scripts such as:

if (101 .. 200) { print; }

The documentation states that this is short for

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

Somehow the use of $. is implicit there (toolic points out in a comment that that's documented too). The idea seems to be that this loop runs once (until $. == 200) in a given instance of the Perl interpreter, and therefore you don't need to worry about resetting the state of the .. flip-flop.

This operator doesn't seem too useful in a more general reusable context, for the reasons you've identified.

旧时浪漫 2024-08-26 03:39:27

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

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

这将保证范围运算符的 RHS 最终为真。

当然,这绝不是一个通用的解决方案。

在我看来,这种行为没有明确记录。如果您可以构建清晰的解释,则可以通过 perlbugperlop.pod 应用补丁。

A workaround/hack/cheat for your particular case is to append the end value to your array:

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

This will guarantee that the RHS of range operator will eventually be true.

Of course, this is in no way a general solution.

In my opinion, this behavior is not clearly documented. If you can construct a clear explanation, you could apply a patch to perlop.pod via perlbug.

め七分饶幸 2024-08-26 03:39:27

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

I found this problem, and as far as I know there's no way to fix it. The upshot is - don't use the .. operator in functions, unless you are sure you are leaving it in the false state when you leave the function, otherwise the function may return different output for the same input (or exhibit different behaviour for the same input).

橘虞初梦 2024-08-26 03:39:27

每次使用 .. 运算符都会维护自己的状态。就像 Alex Brown 所说,当你离开该函数时,你需要将其保留在 false 状态。也许你可以这样做:

sub search {
  my $arr = shift;
  grep { !( /start/ || $_ eq "my magic reset string" ..
            /never_exist/ || $_ eq "my magic reset string" ) } 
      (@$arr, "my magic reset string");
}

Each use of the .. operator maintains its own state. Like Alex Brown said, you need to leave it in the false state when you leave the function. Maybe you could do something like:

sub search {
  my $arr = shift;
  grep { !( /start/ || $_ eq "my magic reset string" ..
            /never_exist/ || $_ eq "my magic reset string" ) } 
      (@$arr, "my magic reset string");
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文