Perl:使用?:运算符时出现意外的匹配变量

4
尝试在Perl中解除一些变量的污染,以下代码效果很好:
if ($year =~ /^(\d{4})$/) {
        $year = $1;
} else {
        &invalid("year");
}

在上述示例中,如果有效,则$1包含$year。然而,当使用?:运算符时,当有效时,$1包含“1”:
($year =~ /^(\d{4})$/) ? $year = $1 : &invalid("year");

有人看到我可能在哪里出错了吗?我很困惑为什么会发生这种情况。这只发生在这台机器上。或者说,多年来我一直成功地使用?运算符返回正确的匹配变量。我还没有在其他机器上尝试过这段代码。

This is Perl, v5.8.8 built for x86_64-linux-thread-multi

3
你确定你运行的是那段代码吗?能否提供一个小的自包含脚本来演示你的问题? - mu is too short
由于优先级问题,条件运算符内的赋值经常是不正确的(例如,c ? x=1 : x=2 意味着 (c ? x=1 : x) = 2)。但是,您提供的代码没有您所描述的问题或任何优先级问题。 - ikegami
OT: 不要使用&来调用子程序,例如&invalid(...);它不会按照你的期望执行...请使用invalid(...) - pavel
@pavel,更准确地说,它的功能超出了大多数人的预期。(它确实调用了子程序,这是预期的行为。) - ikegami
@pavel:在子程序前加上 sigil 会产生什么效果,这不是我期望的(也就是 Perl 4 兼容符号,用于调用可能已经被 Perl 编译器看到或尚未看到的子程序)? - Jonathan Leffler
2
@jonathan:它禁用原型检查,更重要的是,当没有参数使用时,子例程将使用@_数组在调用例程时保持不变... - pavel
3个回答

6
请注意这里的优先级。
比较http://codepad.org/vEPnhWfHhttp://codepad.org/nXVU5CA7,前者按预期工作,而后者不行。当然,我稍微修改了代码以避免调用invalid,但我怀疑这可能是问题的根源。
在两种情况下,都有$ 1包含“2011”,因此也许你应该展示额外的代码,就像mu在第一条评论中请求的那样。
更新
我改变了codepad示例,使用了对&invalid的调用,错误没有出现。
sub invalid {
    print("INVALID\n");
}

sub check_with_if {
    print("Trying with if-statement\n");
    my $year = shift;
    if ($year =~ /^(\d{4})$/) {
        $year = $1;
    } else {
        &invalid($year);
    }
    print("\$1 is $1 and \$year is $year"."\n");
}

sub check_with_conditional {
    print("Trying with conditional expression\n");
    my $year = shift;
    ($year =~ /^(\d{4})$/) ? $year = $1 : &invalid($year);
    print("\$1 is $1 and \$year is $year"."\n");
}

check_with_if("2011");
check_with_conditional("2011");

输出结果为:

Trying with if-statement 
$1 is 2011 and $year is 2011
Trying with conditional expression
$1 is 2011 and $year is 2011

http://codepad.org/z22GMEcn

编辑2:在Mac上的5.12.3也有效。

我同意Jonathan的观点,您可能已经发现了一个在5.8.8中已经修复的错误。如果您非常好奇,可以查看Perl的更改,例如:


有趣的是,在第二个例子中,您有($year =~ /^(\d{4})$/) ? $year = $1 : $year = "invalid";,这会产生invalid。将其更改为($year =~ /^(\d{4})$/) ? $year = $1 : &invalid("year");并且不会调用子程序invalid()。(测试:Perl 5.14.1在MacOS X 10.7.1上)。我不确定我有一个解释。 - Jonathan Leffler
@Jonathan 我想我在我的第一个更新中确实做到了这一点。原因是优先级:赋值比条件低。无效的子程序是使用错误数据调用的:http://codepad.org/RUzgLQDa(除非我没有理解你的评论。) - Ray Toal
经常发生 :) 不知道原帖作者是否会补充问题? - Ray Toal

5
Damien Conway的《Perl最佳实践》解释了使用匹配变量$1等的一些陷阱之一是:
  • 如果正则表达式没有匹配任何内容,$1并不会变成undef,而是保留先前的值(当然,在大多数情况下为undef)
我的直觉告诉我你可能遇到了上下文问题——你的表达式可能在标量上下文中求值,因此当它有效时返回匹配的数量。
因此,我建议尝试像这样重新编写代码:
($year) = ( $year =~ /^(\d{4})$/) or &invalid("year");

1
如果年份无效,这会导致 $year 发生变化。这将防止该值被用于错误消息中。 - ikegami
事实上它具有这种副作用,可以通过我的($validated_year) = ( $year =~ /^(\d{4})$/) or &invalid("year");来避免。 - Tudor Constantin

3

使用Perl 5.14.1(在MacOS X 10.7.1上),此脚本似乎可以正常工作:

use strict;
use warnings;

my $year = "1984";

sub invalid
{
    my($year) = @_;
    print "Invalid year $year\n";
}

if ($year =~ /^(\d{4})$/)
{
    $year = $1;
}
else
{
    &invalid("year");
}


print "Year1: $year\n";

$year = "1984";
($year =~ /^(\d{4})$/) ? $year = $1 : &invalid("year");

print "Year2: $year\n";

它产生:
Year1: 1984
Year2: 1984

如果同样的脚本在Perl 5.8.8版本中产生了不同的答案,那么您可能遇到了已经修复的错误,需要升级。如果Perl 5.8.8生成与Perl 5.14.1相同的答案,则您还没有描述清楚问题,以便我能够理解并重现该问题。


我在一台RHEL 5 x86_64机器上尝试了上述脚本,使用的是系统提供的Perl 5.8.8版本,输出结果与在MacOS X上使用5.14.1版本的相同。我还在RHEL 5上尝试了自己构建的Perl 5.12.1版本,结果也相同。 - Jonathan Leffler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接