嫌がられない三項演算子の書き方
「使うべき」派と「使わないべき」派で意見が分かれる代表格とも言えそうな三項演算子。
自分も学生の頃は使わない方が良いんだろうなあと思って避けていて、入社後1行でスッキリ書けるエレガントさにはまってしまってバンバン使うようになり、リーダブルコードを読んだ後はどっちが良いんだ?となり...
この派閥を行ったり来たりしていました。
なんとなく自分の意見がまとまったので、記事として残しておきます。
メリット
・コード量が減りスッキリ書ける
デメリット
・デバッグがしにくくなる
・理解し辛くなる危険性がある
・馴染みのない人がいる
つまり、なぜ三項演算子が嫌われがちなのか推測すると、
肯定派の中には「三項演算子を使いこなせる俺カッコイイ!」
否定派の中には「この "?" ってどういう処理なんだっけ...」
と、どちらの派閥にも頼りないプログラマーが存在するからだと思います。
とある先輩がこれどっちがどっちだっけ、と聞いてきたときには流石にその人に対する信用を失ってしまいました。 自分が使わないにしてもせめて読めるようにはなっていて欲しいと思います。
まず前者のタイプのプログラマーが書きがちなコード例について書いてみます。 基本的にはコードは短い方が読みやすいはずですが、無理矢理に行数を減らそうとして逆に読みにくくなったパターンです。
int getEntranceFee (int age, int gender){ return (age>=20) ? ((gender==MALE) ? 2000 : 1500) : 1000; }
ネストが深くなっているのも関係して読み辛くなっています。以下のように書いた方がすんなり理解できるはずです。
int getEntranceFee (int age, int gender){ if(age<20) return 1000; else return (gender==MALE) ? 2000 : 1500; }
しかしこう書いたとしても、後者のプログラマーにとっては「結局成人男性だと2000円?1500円?どっちだったっけ?」となります。 知っている人にはこれで全然問題ないはずですが...もう少し読み手に易しくしたいところです。そこで、
#define MALE_FEE 2000 #define FEMALE_FEE 1500 #define CHILD_FEE 1000 int getEntranceFee (int age, int gender){ if(age<20) return CHILD_FEE; else return (gender==MALE) ? MALE_FEE : FEMALE_FEE; }
こう書いてあれば三項演算子部分を見て理解に苦しむ人はほとんどいないでしょう。
いやいや!逆に行数増えてるじゃん!とツッコミが入りそうですが、そもそも入場料は経済の影響で変動する可能性があるので、関数の外で定義しておく方が自然。
あくまで言いたかったのは三項演算子部分でどちらの値が入るかすんなり理解できる必要があるということです。つまり以下の場合はNGです。
#define FEE_1 2000 #define FEE_2 1500 #define FEE_3 1000 int getEntranceFee (int age, int gender){ if(age<20) return FEE_3; else return (gender==MALE) ? FEE_1 : FEE_2; }
別の例でもう少し。
int selling_price = (hasCouponTicket) ? price*0.8 : price; // OK, 「クーポンチケットは割引するもの」という共通認識あり int seat = (hasTicket) ? seat_A : seat_B; // NG, 三項演算子知らない人はどちらか迷う int seat = (hasVipTicket) ? vip_seat : general_seat; // OK, 変数名のおかげで分かりやすくなった int value = (a<b) ? a : b; // NG int min = (a<b) ? a : b; // OK
変数名のつけ方大事だね、という話になってしまった気がしますが、おそらくリファクタリングの概念というのは互いに密接に関わっていると思われます。 こう書けない場合やデバッグのしやすさに影響が出る場合には、無理に三項演算子にする必要はないです。
なお、三項演算子の条件式の部分に括弧を付ける必要はないですが、if文の場合は自然と条件式に括弧が付くので、それに合わせて付けておいた方が個人的には読みやすくなる気がします。(好みの問題)