7

I have the following code:

# 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";
}

This gives the following output:

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

This output is most unexpected! I don't understand why the first ternary seems to always be executing the "if false" clause. It is always assigning a value of 10. I also tried changing the "false" clause to $reps = 6, and I saw that it always got the value of 6. Why does the ternary's logic depend on the content of the third (if false) clause?

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 で導入されました。) "defined-or" 演算子としても知られています。これ:

$x // $y

と同等です

defined($x) ? $x : $y

したがって、次のように書くことができます。

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

;ではなく;を使用して式をテストするため、これはまったく同じセマンティクスを持っていません。存在するが値を持っている場合、動作が異なります。definedexists$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 が指摘するように、すべては優先順位の問題です。条件が false の場合、最終的な割り当ては$x = $value2です。条件が true の場合、割り当ては($x = $value1) = $value2-- いずれにせよ、結果は に割り当て$value2られ$xます。

于 2012-08-06T23:16:50.637 に答える
0

私はこのようにします(三項演算子を使用してもかまいません)

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

HTH

于 2012-08-07T06:37:47.330 に答える
-2

コードのサンプルを提供していただきありがとうございます。これで、バラバラに引き裂くことができます。

三項演算子は使用しないでください。もともと C プログラマーを快適にさせるために残された感染症です。C では、もともと if/else ステートメントよりも効率的だったため、三項演算子が使用されました。ただし、コンパイラはコードの最適化に非常に優れているため、これはもはや真実ではなく、C および C++ プログラミングでは推奨されなくなりました。C でのプログラミングは、三項演算子がなくても十分に難しいものです。

Perl コンパイラは、コードの最適化においても非常に効率的であるため、常に最大限の明確さのために記述する必要があります。これにより、プログラミングが苦手で、コードの保守に行き詰まっている人が仕事を混乱させる可能性があります。

あなたが抱えている問題は、演算子の優先順位の 1 つです。あなたはこれを想定しています:

(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 に答える