2012-03-14 7 views
1

私は完全に混乱しています。ここに私のコードは次のとおりです。なぜ両方のブランチが実行されているようですか?

use strict; 
use warnings; 
use Test::More; 

subtest 'huh?' => sub { 
    my $i = 0; 
    eval { 
     $i++; 
    } || do { 
     $i++; 
    }; 
    is($i, 1, "only execute one branch (i: $i)"); 
}; 

&done_testing(); 

そしてここでは、私のテスト出力は(のActivePerl 5.12、マックOS Xを実行した場合)です。

not ok 1 - only execute one branch (i: 2) 
    # Failed test 'only execute one branch (i: 2)' 
    # at test.pl line 14. 
    #   got: '2' 
    #  expected: '1' 
    1..1 
    # Looks like you failed 1 test of 1. 
not ok 1 - huh? 
# Failed test 'huh?' 
# at test.pl line 15. 
1..1 
# Looks like you failed 1 test of 1. 

ここで何が起こっていますか?私は何もしていないので、最初のブランチだけを実行することを期待しましたdie s。しかし、両方のブランチが実行されたように見えます。

答えて

5

$i++は、$iの値をインクリメントの前に返します。それは0なので、それは間違いです。 evalは評価された最後の結果を返します。これはその偽の値です。それから、次のブロックを試してみる必要があります。

最初のブランチだけを使用する場合は、インクリメントの式「インクリメントを次にが返されます」をeval { ++$i }のようにする必要があります。

さて、evalが失敗したかどうかを確認するための最良の方法は、次の操作を実行するのではなく、1を返すことではありません。

  1. [email protected](または$English::EVAL_ERROR)をローカライズ
  2. evalの
  3. テスト[email protected]

    local [email protected]; 
    eval { $some_sub->(); }; 
    croak("Failed in evaluate: [email protected]") if [email protected]; 
    
+0

ああ、例外をトラップするために 'eval'を使用する場合、' eval'の内部に例外がスローされなかった場合、必ず真の値を返すようにしてください。 –

+0

@MattFenwick、あなたが '||'または 'or'で戻り値をテストしようとしているのなら、うまくいきます。私は私の答えを更新するつもりです。 – Axeman

+1

バグを修正する前にperlのバージョンで実行している場合、 '$ @'が 'DESTROY'ブロックでクリアされてから' if $ @ 'ステートメントが実行される可能性があります。 'eval {...; 1} or croak ... 'はそのバグに苦しまない。 –

2

eval { $i ++ }は、が返す同じ結果を返します。つまり、インクリメントされる前の値は$iです。 $i0に設定すると、0が返されます。これはfalseです。つまり、||の右側のオペランドも評価されます。

は、私はあなたが[email protected]に保存されeval、内dieの結果と(単に含まれる式の結果である)evalを返された結果が混乱していると思います。

+0

はい、私はrefactorに 'eval'の戻り値の代わりに' $ @ 'をチェックするために私のコードベースに行かなければならないと思います。 –

+0

またはTry :: Tinyを使用してください: –

+0

@briandfoy:はい、[TIMTOWTDI](http://en.wikipedia.org/wiki/There's_more_than_one_way_to_do_it) –

関連する問題