有一个perl脑筋急转弯:
my @l = ('a','b','c'); for (@l) { my $n = 1; print shift @l while (@l and $n --> 0); print "\n"; }
什么是印刷品?应该是a,b和c,对吗?但哦等等,实际上某处有一个错误,它只打印a和b.可能只是一些愚蠢的一个一个,应该很容易解决,对吧?
好吧,做一个小的代码更改来测试并改变@l到
my @l = ('a','c','d');
什么是印刷品?可能是a,b和c因为那个愚蠢的一个,对吗? ……等一下,实际上它仍然只打印a和b.好的,所以错误是它只打印前两个字符.
再次将@l更改为
my @l = ('a','d','e');
嗯,现在打印a,b和c.但不是d或e.实际上,我们从现在开始添加的每两个字母都会使它打印出序列中的下一个字母.因此,如果我们添加f,它仍然只是打印a,但如果我们添加f和g,它将打印a,b,c和d.
对于$n的不同值,这也会发生类似的结果.
那么这里发生了什么?
解决方法
我认为这是某人的小工具代码.它看起来不像你想要写任何东西的方式.但它最好的说明是(至少在某些版本中)Perl实际上运行的是更基本的for循环,其中:
for ( @l ) { #... }
替换为:
for ( my $i = 0; $i < @l; $i++ ) { local $_ = $l[$i]; #... }
因此,因为@l是(‘c’)当我们经历了两次时,我们的行程已经超过了标量(@l),所以我们就出局了.我已经在很多情况下对它进行了测试,它们似乎是等价的.
下面是我为测试用例编写的代码.从中我们可以看到,由于这种转变,一旦我们走了一半,循环就会退出.
use strict; use warnings; use English qw<$LIST_SEPARATOR>; use Test::More 'no_plan'; sub test_loops_without_shifts { my @l = @_; my @tests; for ( @l ) { push @tests,$_; } my @l2 = @_; my $n = @tests; my $i = 0; for ( $i = 0; $i < @l2; $i++ ) { local $_ = $l2[$i]; my $x = shift @tests; my $g = $_; is( $g,$x,"expected: $x,got: $g" ); } is( $n,$i ); is_deeply( \@l,\@l2,do { local $LIST_SEPARATOR = .','; "leftover: ( @l ) = ( @l2 )" } ); return $i; } sub test_loops { my @l = @_; my @tests; for ( @l ) { push @tests,shift @l; } my @l2 = @_; my $n = @tests; my $i = 0; for ( $i = 0; $i < @l2; $i++ ) { local $_ = $l2[$i]; my $x = shift @tests; my $g = shift @l2; is( $g,do { local $LIST_SEPARATOR = ','c; "leftover: ( @l ) = ( @l2 )" } ); return $i; } is( test_loops( 'a'..'c' ),2 ); is( test_loops( 'a'..'d' ),2 ); is( test_loops( 'a'..'e' ),3 ); is( test_loops( 'a'..'f' ),3 ); is( test_loops( 'a'..'g' ),4 ); is( test_loops_without_shifts( 'a'..'c' ),3 ); is( test_loops_without_shifts( 'a'..'d' ),4 ); is( test_loops_without_shifts( 'a'..'e' ),5 ); is( test_loops_without_shifts( 'a'..'f' ),6 ); is( test_loops_without_shifts( 'a'..'g' ),7 );