2009-08-03 19 views
1

エラーチェックを管理するPerlルーチンがあります。前の成功に基づいて、約10の異なるチェックがあり、いくつかはネストされています。これらは通常、私がcroak/dieにする必要がある例外的なケースではありません。また、エラーが発生すると、残りのチェックを実行する必要はありません。Perlでエラーチェックをきれいに処理できますか?

しかし、私は次の恐ろしいハックに類似したものを使用する場合を除き、この問題を解決するためにきちんとした方法を考えるように見えることはできません。

sub lots_of_checks 
{ 

if(failcond) 
{ 
    goto failstate: 
} 
elsif(failcond2) 
{ 
    goto failstate; 
} 

#This continues on and on until... 

return 1; #O happy day! 

failstate: 

return 0; #Dead... 
} 

私が行うことができることを好むだろう何だろうそのような何か:

do 
{ 
if(failcond) 
{ 
    last; 
} 
#... 
}; 
+1

goto failstateを使用するのではなく、すぐに戻るのはなぜですか?見た目はきれいではありませんが、においは少なくなります。 – lexu

+0

私は「シングルエントリー、シングルエグジット」を保持することを望みます。それは可能ではないかもしれません...私は代替案を模索しようとしています。 –

+3

Perlコミュニティでは、一般的にループ/サブ/ etcから抜け出すのがより良いフォームと考えられています。早い。ブロックの残りの部分が指定された条件に適用されないことが明確になります。 –

答えて

12

空のreturn文は0を返すよりもPerlのサブからfalseを返す良い方法です。後者の値は実際にはリストコンテキストでtrueになります:あなたの例を考えると

sub lots_of_checks { 
    return if fail_condition_1; 
    return if fail_condition_2; 
    # ... 
    return 1; 
} 
+0

これは、 'cond {;ならば、undefを返す 'と同じですか? –

+4

@Paulの場合、空の戻り値はスカラーコンテキストではundefを返し、空のリストはリストコンテキストです。 – daotoad

4

、私はこのように書くと思います:

sub lots_of_checks { 
    local $_ = shift; # You can use 'my' here in 5.10+ 

    return if /condition1/; 
    return if /condition2/; 
    # etc. 

    return 1; 
} 

returnの代わりreturn 0に注意してください。これは、文脈を尊重しているため、通常はより良いです。スカラーコンテキストではundef、リストコンテキストでは()(空のリスト)となります。

シングル出口ポイント(少しPerlishではありません)を保持したい場合は、gotoに頼らずに実行できます。 lastのドキュメントでは、

...というように、ブロック自体は、一度実行されるループと意味的に同じです。 したがって、「最後」を使用して、そのようなブロックから早期に終了することができます。

sub lots_of_checks { 
    local $_ = shift; 
    my $all_clear; 

    { 
     last if /condition1/; 
     last if /condition2/; 
     # ... 
     $all_clear = 1; # only set if all checks pass 
    } 

    return unless $all_clear; 
    return 1; 
} 
+0

'local $ _;' 'local $ *;を使ってはいけません。今は参照を見つけることができませんが、ブライアンdフォイがそれを引用できると確信しています。 –

+1

perlmonksから:$ _が結ばれているsometingにエイリアスまたはエイリアスされていて、pos($ _)を保護していない場合、ローカル$ _はうまく動作しません。この場合、 'local $ _ = shift;'私は 'my($ arg)= @ _を好むほど珍しいです。 for($ arg){/ condition1/etc etc} 'と書かれています。 –

+1

最初の例で対称性を保持することで、2番目の例のメカニズムをより明確にすることを考えた疑似スイッチの構文を避けました。 '$ _ 'のローカライズに関しては、' local'の振る舞いがタイプグロブにひどく絡んでいることは残念です。私は、エクスポートされたパッケージ変数をローカライズしようとする前に問題に遭遇しました。安全な(そして醜い)代替案は、グロブ全体をエクスポート/ローカライズすることです。ループの実装は 'local $ _'または' local $ * 'とは何ですか?どちらかが問題を引き起こしているのを見ることができます... –

5

あなたは絶対にあなたが好む何を行うことができます。

Check: { 
    last Check 
     if failcond1; 
    last Check 
     if failcond2; 
    success(); 
} 
2

あなたは/単一構造をあなたのシングルを残しておきたい場合は、あなたが得るために少し他の提案を変更することができます。

IMO
sub lots_of_checks 
{ 
    goto failstate if failcond1; 

    goto failstate if failcond2; 

    # This continues on and on until... 

    return 1; # O happy day! 

    failstate: 

    # Any clean up code here. 

    return; # Dead... 
} 

、EXPR if文修飾子フォーム「リターンのPerlの使用"ガード句はCよりも読みやすくなります。最初にラインを見ると、ガード句があることがわかります。この機能はしばしば侮辱されていますが、この場合はかなり好きです。

gotoをステートメント修飾子とともに使用すると、単一の終了コードスタイルが維持されますが、明瞭さが維持され、クラッタが軽減されます。私は、このフォームを、ルーチンの検証に失敗した後に複雑なクリーンアップをしたときに使用しました。

5

なぜ例外は使用しませんか?コードの通常の流れが守られていない場合は例外です。 "return"や "goto"を使うのは本当に同じことです。ちょうど "あなたが望むものではありません"。

(本当に欲しいのは、 "return"、 "goto"、 "last"、 "throw"のすべての特殊なケースです。Perlには完全な継続はありませんが、 http://metacpan.org/pod/Continuation::Escape

あなたのコードの例では、あなたが書く:あなたはトリッキーなことと無視する場合

eval { 
    if(failcond){ 
     die 'failcond'; 
    } 
} 

do 
{ 
if(failcond) 
{ 
    last; 
} 
#... 
}; 

をこれはおそらく同じですその他の例外:

my $magic = []; 
eval { 
    if(failcond){ 
     die $magic; 
    } 
} 
if ([email protected] != $magic) { 
    die; # rethrow 
} 

また、上記のContinuation :: Escapeモジュールを使用することもできます。しかし、 例外を無視する理由はありません。この方法でそれらを使用することは完全に受け入れられる です。

関連する問題