【问题标题】:Breaking out of loop causes different results [duplicate]跳出循环会导致不同的结果[重复]
【发布时间】:2015-04-02 04:21:32
【问题描述】:

我有这个代码:

#!/usr/bin/perl
use strict;
use warnings;

my $judge_exes = {
    "^A" => "foo",
    "^B" => "bar",
    "^C" => "baz",
};

sub get_judge {
    my ($test_id) = @_;
    my $exe = undef;
    while (my ($regex, $judge) = each %$judge_exes) {
        if ($test_id =~ /$regex/) {
            $exe = $judge;
            last;
        }
    }

    if ($exe) {
        return $exe;
    } else {
        return "Undefined!";
    }
}

print get_judge("A1");
print get_judge("B2");
print get_judge("C3");

(ideone:http://ideone.com/slxebG)

我希望得到foobarbaz 的输出,但我最终得到fooUndefined!baz。但是,当我注释掉 last 语句时,我得到了正确的行为:

#!/usr/bin/perl
use strict;
use warnings;

my $judge_exes = {
    "^A" => "foo",
    "^B" => "bar",
    "^C" => "baz",
};

sub get_judge {
    my ($test_id) = @_;
    my $exe = undef;
    while (my ($regex, $judge) = each %$judge_exes) {
        if ($test_id =~ /$regex/) {
            $exe = $judge;
            # last;
        }
    }

    if ($exe) {
        return $exe;
    } else {
        return "Undefined!";
    }
}

print get_judge("A1");
print get_judge("B2");
print get_judge("C3");

(ideone:http://ideone.com/QJpxbK)

为什么会这样? (我在 Perl 5.16.2 上,但问题也出现在 5.10.1 和任何 ideone 使用的版本上。)

  • 据我了解,last 只是跳出了while 循环,这正是我想要的。
  • $exe 似乎不是导致我打错 if 分支的替代错误值。 (我可以早点回来,这样会更好,但我还是不明白这是什么原因。)
  • 我认为我正确地取消引用了哈希引用。
  • 我认为正则表达式匹配不会对循环终止产生任何副作用。
  • 正则表达式似乎匹配正确,因为在某些情况下我实际上可以让它们正确匹配,所以我认为这不是插值问题。
  • 我没有修改我正在迭代的容器。

我只是犯了一些与 Perl 无关的愚蠢错误吗?

【问题讨论】:

    标签: regex perl


    【解决方案1】:

    你变得迷茫了。我不确定混淆在哪里,但您必须知道 each 运算符在调用之间保持其状态。这意味着您在 while 循环内的 last 不会终止迭代。相反,它会在下次遇到时从上次中断的地方继续。

    您还应该避免使用双引号,因为它们会插入任何标量或数组变量或反斜杠控制字符。

    这是我建议您编写算法的方式

    #!/usr/bin/perl
    use strict;
    use warnings;
    use 5.010;
    
    my $judge_exes = {
        '^A' => 'foo',
        '^B' => 'bar',
        '^C' => 'baz',
    };
    
    say get_judge('A1', $judge_exes);
    say get_judge('B2', $judge_exes);
    say get_judge('C3', $judge_exes);
    say get_judge('D4', $judge_exes);
    say get_judge('E5', $judge_exes);
    
    sub get_judge {
        my ($test_id, $judges) = @_;
    
        for my $re ( keys %$judges ) {
            return 1 if $test_id =~ /$re/;
        }
    
        'Undefined!';
    }
    

    输出

    1
    1
    1
    Undefined!
    Undefined!
    

    【讨论】:

      【解决方案2】:

      哦,看起来行为是explained here

      在标量上下文中使用keys %hash 会返回散列中的键数并重置与散列关联的迭代器。如果您使用last 提前退出循环,则可能需要执行此操作,以便当您重新进入循环时,哈希迭代器已被重置。

      没想到 - 我以为 each 会重置迭代器。

      【讨论】:

      • perldoc -f each中也有解释
      • 这肯定是问题所在。当我添加诊断打印时,似乎each 没有在循环的后续条目中被重置(使用 Perl 5.20.0 进行测试)。就“'each`没有重置”而言,我已经得到了;我没有得到它的文档。完全违反直觉!
      • OP的代码没有使用keys,这就是问题所在,这是一个奇怪的引用来说明原理。 keys %hash 生成一个列表,您可以使用 for 进行迭代,而与散列的任何更改无关。但是each %hash 每次调用时都会返回一个新的键/值对,无论上下文如何,任何好的迭代器都应该
      • @JonathanLeffler:之所以会产生混淆,是因为 Perl 没有内置的 iterator 对象,并试图匿名进行。 Perl 的keysvalues 生成静态列表,而each 创建一个与哈希绑定的匿名迭代器对象,并且在下次调用同一哈希上的each 之前不会改变状态。 Python 和 Lua 做同样的事情,任何以C 开头的东西都没有机会
      • @Borodin:这很有意义;我以前根本没有遇到过它,要么是因为我让迭代器完成,要么是因为我正在使用非全局哈希,因此每次调用该函数时,它都在使用不同的哈希,因此 each重新开始。或者纯粹是运气不好......我没有注意,或者碰巧从来没有遇到过问题。第一次遇到它有点违反直觉,但一旦你知道发生了什么就足够有意义了。这是关于 SO 的好处之一;你可以从中学习。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-12
      • 1970-01-01
      相关资源
      最近更新 更多