Hatena::ブログ(Diary)

Guinea Pig このページをアンテナに追加 RSSフィード

 | 

2013-02-20

[][][]三項演算子である条件演算子が右結合であることの利点・妥当性と可読性について

条件演算子とは?

 条件演算子とは、よく見るアレのことである。

bool b = true;
string s = b ? "真" : "偽" ; // ここで出てくる ? と : が条件演算子
// ? の左が真であれば : の左を返し、
// ? の左が偽であれば : の右を返す。
// この場合 b が true なので (b ? "真" : "偽") は "真" を返す

右結合と左結合

 んで、右結合、左結合というのは、同じ優先度の演算子が並んだ場合、それを右からまとめていくか左からまとめていくかと言うルールの話である。

// 左結合の例
int sub = 10 - a - b - c;
// 左結合なので、以下の順番で解釈される
// int sub = (((10 - a) - b) - c);

// 右結合の例
x = y = z = 10;
// 右結合なので、以下の順番で解釈される
// x = (y = (z = 10));
// ちなみに、代入演算子は代入した内容が返るので、 
// (z = 10) は (zに10を代入した後に) 10 を返し、 
// y = (z = 10) は y にも 10 を代入することになる。

条件演算子は何結合?

 んでんで、条件演算子が右結合か左結合か、というのは、以下のケースをどのように解釈するのか? と言う話になるわけです。

string s = b1 ? "真" : b2 ? "やっぱり真" : "偽" ; 
// 右結合の場合 (ほとんどの言語ではこれ)
// string s = b1 ? "真" : (b2 ? "やっぱり真" : "偽") ; 
// b1 が true なら "真" false なら → (b2 が true なら "やっぱり真" false なら "偽") を返す

// 左結合の場合 (PHPではこれ)
// string s = (b1 ? "真" : b2) ? "やっぱり真" : "偽" ; 
// (b1 が true なら "真" false なら b2) が true なら "やっぱり真" false なら "偽" を返す

 右結合の場合、以下のような書き方が可能です。

int result = 
    b1 ? 1 :
    b2 ? 2 :
    b3 ? 3 :
    b4 ? 4 :
    b5 ? 5 :
    -1;

 これは、以下の表現と等価です。

int result = 0;
if (b1) {
    result = 1;
} else if (b2){
    result = 2;
} else if (b3){
    result = 3;
} else if (b4){
    result = 4;
} else if (b5){
    result = 5;
} else {
    result = -1;
}

 これを知ってれば、条件演算子を重ねても、if文からの連想で、それなりの可読性があるんじゃないでしょーか。

 とゆーわけで、条件演算子が右結合であるとこんな書き方ができます、と言うあたりが利点なのかなー、と思いました。


条件演算子を重ねると右結合の方が有利と思われるって話

 条件演算子を使って可読性に難があるかなぁ、と思うパターンとしては以下のような書き方です。

int result = b1 ? b2 ? 1 : 2 : b3 ? 3 : 4;

 そして、これもまた右結合と左結合で解釈が異なります。

// 右結合の場合の解釈
// int result = b1 ? (b2 ? 1 : 2) : (b3 ? 3 : 4);

// 無理すると、これは以下のよう記述にすることによって可読性を上げられる
// int result = b1 ? // b1 の真偽によって上か下かに振り分ける
//     (b2 ? 1 : 2) :  // b1 が真の時は b2 の真偽によって結果が決まる
//     (b3 ? 3 : 4) ;  // b1 が偽の時は b3 の真偽によって結果が決まる


// 左結合の場合の解釈
// int result = (b1 ? (b2 ? 1 : 2) : b3) ? 3 : 4;
// おそらく、この状況になるとかなり直感的ではない。

 条件演算子でコードを書こうとする場合、記述者は「?の手前に条件を書こう」「その真偽によって決まる値は:の左右に振り分けよう(つまり、:の左右は同じように扱われて欲しい)」と考えるわけです。

 このとき、条件演算子が右結合であれば

int result = b1 ? b2 ? 1 : 2 : b3 ? 3 : 4;

 が

int result = b1 ? 
    b2 ? 1 : 2 
    : // この : に対して、その前後にあるものが同じようにくっついて扱われる
    b3 ? 3 : 4;

 となり、書き方によって(同じくif文からの連想で)それなりに直感的になるわけです。


 仮に左結合だとして、無理にそれを意図した書き方をすると

int result = b1 ? // b1の結果で振り分ける
        b2 ? 1 : 2  // b1 が真なら 1 か 2
        : 
        b3 ? // b2 が偽なら b3
    3  // ↑の結果で、最終的に 3 か 4 が返却されます。
    : 
    4;

 こんな感じになります。おそらくかなり読みづらく、そしてこんな条件判断に基づく処理を行う機会はそうそうありません。

 よって、

int res1 = 
    b1 ? 1 :
    b2 ? 2 :
    b3 ? 3 :
    b4 ? 4 :
    b5 ? 5 :
    -1;

int res2 = 
    c1 ?
        c11 ? 
            c111 ? 1 : 2
            :
            c112 ? 3 : 4
        :
        c12 ? 
            c121 ? 5 : 6
            :
            c122 ? 7 : 8

 このような書き方ができて、それがif文と似た感じのくっつき方になるので(条件演算子がif文を意識したものであるなら)右結合の方が妥当かな、と思ったわけです。

まとめ

 条件演算子を重ねる前に別の書き方を考えよう

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/murishinai/20130220/p1
 |