7

我有以下代码:

# List of tests
my $tests = [("system_test_builtins_sin", "system_test_builtins_cos", "system_test_builtins_tan")];

# Provide overrides for certain variables that may be needed because of special cases
# For example, cos must be executed 100 times and sin only 5 times.
my %testOverrides = (
    system_test_builtins_sin => {
        reps => 5,
    },
    system_test_builtins_cos => {
        reps => 100,
    },
);

my %testDefaults = (
    system_test_reps => 10,
);

# Execute a system tests
foreach my $testName (@$tests)
{
    print "Executing $testName\n";
    my $reps;

    if (exists $testOverrides{$testName}{reps})
        { $reps = $testOverrides{$testName}{reps}; }
    else
        { $reps = $testDefaults{system_test_reps}; }
    print "After long if: $reps\n";
    exists $testOverrides{$testName}{reps} ? $reps = $testOverrides{$testName}{reps} : $reps = $testDefaults{system_test_reps};
    print "After first ternary: $reps\n";
    exists $testOverrides{$testName}{reps} ? $reps = $testOverrides{$testName}{reps} : print "Override not found.\n";
    print "After second ternary: $reps\n";
}

这给出了以下输出:

Executing system_test_builtins_sin
After long if: 5
After first ternary: 10
After second ternary: 5
Executing system_test_builtins_cos
After long if: 100
After first ternary: 10
After second ternary: 100
Executing system_test_builtins_tan
After long if: 10
After first ternary: 10
Override not found.
After second ternary: 10

这个输出是最出乎意料的!我不明白为什么第一个三元似乎总是在执行“if false”子句。它总是赋值10。我也尝试将“false”子句更改为$reps = 6,我看到它总是得到6的值。为什么三元的逻辑取决于第三个(如果为假)子句的内容?

4

4 回答 4

10

这是一个更简单的脚本来说明问题:

#!/usr/bin/perl

use strict;
use warnings;

my $x;

1 ? $x = 1 : $x = 0;
print "Without parentheses, \$x = $x\n";

1 ? ($x = 1) : ($x = 0);
print "With parentheses, \$x = $x\n";

它产生这个输出:

Without parentheses, $x = 0
With parentheses, $x = 1

我不确定赋值和运算符之间的关系是否?:可以通过运算符优先级来完整解释。(例如,我相信 C 和 C++ 在某些情况下会表现不同。)

运行perldoc perlop并搜索“条件运算符”,或查看此处;它涵盖了这个确切的问题(比我在这里更简洁)。

无论如何,我认为使用if/else语句会比使用?:运算符更清晰。或者,由于“true”和“false”分支都分配给同一个变量,更好的用法?:是改变这个:

exists $testOverrides{$testName}{reps}
    ? $reps = $testOverrides{$testName}{reps}
    : $reps = $testDefaults{system_test_reps};

对此:

$reps = ( exists $testOverrides{$testName}{reps}
          ? testOverrides{$testName}{reps}
          : $testDefaults{system_test_reps} );

但同样,我不得不换行以避免滚动这一事实很好地表明 if/else 会更清晰。

您也可以考虑使用//操作符,除非您遇到不支持它的古老版本的 Perl。(它是由 Perl 5.10 引入的。)它也被称为“定义或”运算符。这:

$x // $y

相当于

defined($x) ? $x : $y

所以你可以写:

$reps = $testOverrides{$testName}{reps} // $testDefaults{system_test_reps};

这没有完全相同的语义,因为它使用defined而不是exists;来测试表达式。$testOverrides{$testName}{reps}如果存在但具有值,它将表现不同undef

于 2012-08-06T22:54:52.177 回答
8

-p选项对以下B::Deparse问题很有启发性:

$ perl -MO=Deparse,-p -e '$condition ? $x = $value1 : $x = $value2'
(($condition ? ($x = $value1) : $x) = $value2);

正如 Keith Thompson 所指出的,一切都与优先级有关。如果条件为假,则最终赋值为$x = $value2。如果条件为真,则分配是($x = $value1) = $value2——无论哪种方式,结果都是分配$value2$x

于 2012-08-06T23:16:50.637 回答
0

我会这样做(我不介意使用三元运算符)

$reps = exists($testOverrides{$testName}{reps}) ?
    $testOverrides{$testName}{reps}             :
    $testDefaults{system_test_reps};

高温高压

于 2012-08-07T06:37:47.330 回答
-2

感谢您向我们提供您的代码示例。现在我们可以把它撕成碎片。

不要使用三元运算符。这是一种最初让 C 程序员感到舒服的感染。在 C 中,使用三元运算符是因为它最初比 if/else 语句更有效。但是,编译器在优化代码方面做得很好,所以这不再适用,现在在 C 和 C++ 编程中不鼓励这样做。用 C 语言编程已经够难了,因为它没有三元运算符。

Perl 编译器在优化代码方面也非常有效,因此您应该始终以最清晰的方式编写代码,这样其他编程能力不如并在维护您的代码时遇到困难的人可能会在他们的工作中一头雾水。

您遇到的问题是运算符优先级之一。你假设这个:

(exists $testOverrides{$testName}{reps}) 
    ? ($reps = $testOverrides{$testName}{reps}) 
    : ($reps = $testDefaults{system_test_reps});

我也会的。毕竟,这就是我的意思。但是,赋值运算符的优先级低于三元运算符。真正发生的是这样的:

(exists $testOverrides{$testName}{reps}) 
    ? ($reps = $testOverrides{$testName}{reps}) : ($reps))
    = $testDefaults{system_test_reps});

所以,最后的任务总是发生在$reps.

如果你使用 if/else 会更好:

if (exists $testOverrides{$testName}{reps}) {
   $reps = = $testOverrides{$testName}{reps};
}
else {
   $reps = $testDefaults{system_test_reps};
}

没有优先级问题,更容易阅读,同样高效。

于 2012-08-07T04:07:51.280 回答