Entry

プログラミングメモ - 古典的だけど while-文で break だけ使うの話

2008年09月09日

ケースバイケースなんでしょうけど,こちらから。

関数の中にreturnを複数書いてはいけない、というルールはよく見かけます。
拒絶反応を起こす人もよく見ます。自分もそうです。
インデントが深くなるのは見辛くて駄目だ。こんなのナシだ!! とルールに立ち向かう人がいました。
ここではレジスタンスと呼ぶことにします。
レジスタンスもたくさんいるわけですが、その中でも感動した方法は次のものです。

BOOL func( void )
{
  BOOL bRet = FALSE;

  while( 1 ){
    if( !test1() ){
      break;
    }
    if( !test2() ){
      break;
    }
    if( !test3() ){
      break;
    }
    if( !test4() ){
      break;
    }
    if( !test5() ){
      break;
    }
    testOKfunc();  // 条件を満たせばできる処理
    bRet = TRUE;
    break;
  }
  return bRet;
}

ガード句を使いながら、returnはひとつだけです。
このような書き方を "カラwhile" と呼んでいました。whileの最後でbreakする(=ループしない)からだと思います。
もちろん別の戦地ではこんな言葉通じません。

カラwhileって使いますか?

こゆのは普通に,

BOOL
func(void) {
    bRet = FALSE;
    if (test1() && test2() && test3() && test4() && test5()) {
        testOKFunc();
        bRet = TRUE;
    }
    return bRet;
}

で,ええんでないの?

条件式が長くなるのはイヤンって場合は,マクロなり,インライン関数にしてしまえばいい(普通の関数でもいいけど)。以下はインライン関数の例。

inline BOOL
isTestOK() {
    return (test1() && test2() && test3() && test4() && test5());
}

BOOL
func(void) {
    bRet = FALSE;
    if (isTestOK()) {
        testOKFunc();
        bRet = TRUE;
    }
    return bRet;
}

実は test?() は引数がたくさんあって条件式に書くとゴチャゴチャになっちゃうのよ,という場合は isTestOK() をこんな風に書けばいいか。

inline BOOL
isTestOK(const Some& someArg, const Another& anotherArg) {
    BOOL bRet = TRUE;

    bRet = (bRet && test1(somearg.foobar(), 0x00, anotherArg));
    bRet = (bRet && test2(somearg.foobar(), 0x10, anotherArg));
    bRet = (bRet && test3(somearg.foobar(), 0x20, anotherArg));
    bRet = (bRet && test4(somearg.foobar(), 0x30, anotherArg));
    bRet = (bRet && test5(somearg.foobar(), 0x40, anotherArg));

    return bRet;
}

bRet が FALSE の場合も test2() 以降が実行されちゃいそうだけど,&& 演算子が入っているから,bRet がどこかで FALSE になった時点で後の関数が実行されることはありません。ジャンプするわけじゃなくて,bRet が何度も評価されるので,厳密に等価な文じゃないんですけどね。けど,これなら,条件の追加や削除も楽ちんです。

また,実は test2() が FALSE の場合は後処理を実行したいのよ,という場合もあると思います。その場合は,こうなるか。

inline BOOL
isOK() {
    BOOL bRet = TRUE;

    bRet = (bRet && test1());
    bRet = ((bRet && test2()) || (afterProc() && FALSE));
    bRet = (bRet && test3());
    bRet = (bRet && test4());
    bRet = (bRet && test5());

    return bRet;
}

後処理関数である afterProc() に何か値を戻してもらう制限がつくけれど,大した制限じゃないと思います(値を戻さない場合はコンパイルエラーになるから,間違えて実装しちゃうこともないだろうし)。まぁ,ここまで来ると,ちとテクニカルになって可読性が落ちるんですけどね……。

実際のところ,goto を使うか while を使うか迷う場合ってのはあまりない感じがします。こゆときに安易に goto やらカラ while やらを使うのが多いから,大抵のコーディング規約で禁止してるんだろうな……多分。

Trackback
Trackback URL:
Ads
About
Search This Site
Ads
Categories
Recent Entries
Log Archive
Syndicate This Site
Info.
クリエイティブ・コモンズ・ライセンス
Movable Type 3.36
Valid XHTML 1.1!
Valid CSS!
ブログタイムズ

© 2003-2012 AIAN