【问题标题】:Perl do...while and last commandPerl do...while 和 last 命令
【发布时间】:2016-05-18 22:56:14
【问题描述】:

我刚刚遇到了一些我真的无法解释的非常奇怪的行为:

do {
    my $qry = $self->getHTMLQuery(undef, $mech->content());
    next if (!defined($qry));

    push(
        @prods,
        map { 'http://www.XXXXYYYX.com'.$_->attr('href') }
            $qry->query('div.prodInfo div.prodInfoBox a.prodLink.GridItemLink')
    );

    $qry->delete();
    $TEST++;

    last if ($TEST >= 10);

} while(eval { $mech->follow_link(class => 'jump next') });

print "WHILE ENDED\n";

上面的代码从不打印“WHILE ENDED”,即使当$TEST >= 10 时它似乎退出了 while 循环。

但下面的代码确实会打印“WHILE ENDED”:

do {
    my $qry = $self->getHTMLQuery(undef, $mech->content());
    next if (!defined($qry));

    push(
        @prods,
        map { 'http://www.XXXXYYYX.com'.$_->attr('href') }
            $qry->query('div.prodInfo div.prodInfoBox a.prodLink.GridItemLink')
    );

    $qry->delete();
    $TEST++;

} while(eval { $mech->follow_link(class => 'jump next') } && $TEST <= 10);

print "WHILE ENDED\n";

在这两个测试中,$TEST 的初始值为 0。

do...while 中的last 的行为与forwhile {...} 中的行为是否不同?

【问题讨论】:

  • 我假设您第二次说“上面的代码”,实际上是指“代码下面
  • do {...} while 循环位于真正的while {...} 中时,情况会更糟。您尝试在内部循环中使用lastnext,但它们会影响外部循环。在你意识到这个 Perl 陷阱之前,这是一个 WTF 情况。顺便说一句,它在perltrap doc 中列出。

标签: perl


【解决方案1】:

nextlastredo 而言,带有循环修饰符的do 块不算作真正的循环。 perlsyn 中提到了这一点,您会在其中找到 Schwern 提到的关于用裸块包围它以使 last 工作的技巧。但这不适用于next,因为一个裸块只执行一次,所以next 的行为类似于last。要使next 工作,您可以将裸块放入 do,但随后last 将像next 一样工作。

如果您需要nextlast 来处理do ... while,最简单的方法是在continue 块中使用具有真实条件的无限循环。这两个循环是等价的,只是第二个是一个真正的循环,所以它与next & last 一起工作:

do { ... } while condition;
while (1) { ... } continue { last unless condition };

【讨论】:

    【解决方案2】:

    来自perldoc -f last

    "last" 不能用于退出返回值的块,例如 “eval {}”、“sub {}”或“do {}”

    【讨论】:

      【解决方案3】:

      TLP 是对的。解决这个问题的标准解决方法(我自己打的)是将 do/while 包装在一个裸块中,这与直觉相反,确实尊重循环控制。

      { do {
          last;
      } while 1; }
      

      外面的块会捕获last。如果你想处理next,你必须把集团放进去。

      do {{
          next;
      }} while 1;
      

      里面的块会捕获next

      很遗憾,你不能两者兼得。

      【讨论】:

      • 这将修复last,但不会修复next。对于裸块,这两个是等价的(因为裸块只执行一次)。
      • @cjm 很好。我发现你不能两者都做。 :-(
      • 嗯,你可以,但你必须在外块上贴一个标签,然后使用last LABEL。这只是在询问错误,因为如果您忘记了标签并只输入last,它的行为就会像next
      猜你喜欢
      • 2013-12-01
      • 1970-01-01
      • 2015-07-03
      • 1970-01-01
      • 2015-05-24
      • 1970-01-01
      • 2014-10-03
      • 1970-01-01
      • 2021-07-01
      相关资源
      最近更新 更多