コードレビューをしているときに以下のようなコードが現れて、
for ($i = 0; ! ($i > $limit || $i >= count($list)); $i++) { // $list[$i]; を使って何かする処理 }
このfor文の条件を理解するのに時間が少しかかりました。
要は、
- 最大で$limit回
- 最小で$listの要素数分
ループ中の処理が行われる
というものになるのですが、ぱっと見て分かりますか?自分は分からなさ過ぎて、自分で別途デバッグしてしまいましたorz
それにしても、なんでこんなに分かりづらいのかなぁ、僕がアホなだけなのかなぁと、小一時間考えてみたところ、
- 条件が(複数)あってさらに否定がかまされている
- 結局どっちやねんとなってしまう
という部分が分かりづらい要因なんじゃないかと自分は思ったわけです。
(自分がアホなだけという部分も否定しませんが)
では、上記のコードが以下だったらどうでしょうか。
否定だけを取っ払ってみる
for ($i = 0; $i > $limit || $i >= count($list); $i++) { }
こっちのほうが、まだわかりやすくないですか?
自分はわかりやすいです。
でも、これだと否定だけ取っ払っただけで、中身の条件も変えないと最初の条件と変わってしまいます。
うーーん。どうすればいいんだろう。。
ド・モルガンの法則
そこで奥さん、ド・モルガンの法則 ですよ。
え、ド・モルガンの法則? モーガン・フリーマンとか関係あるん? と思うかもしれませんが
ド・モルガンの法則 – Wikipedia を見てみると、こういったことが書いてあります。
「真と偽を入替え、論理和を論理積を入替えた論理体系」は、元の論理体系と同一視できる
C言語などプログラミング言語の記号を使って書けば、P, Q がどんな式であろうと
!(P || Q) == !P && !Q
!(P && Q) == !P || !Qが成立するということである。
おぉ、なんかよく分からないけど今回のそれっぽいと思いませんか?
それでは、このテンションのまま法則に従ってみます。
ド・モルガンの法則に従ってみる
最初のコードをド・モルガンの法則に従ってみるとこうなります。
最初のコード
for ($i = 0; ! ($i > $limit || $i >= count($list)); $i++) { }
ド・モルガンの法則
for ($i = 0; ! ($i > $limit) && ! ($i >= count($list)); $i++) { }
どうでしょうか。
後者(ド・モルガンの法則)のほうがまだ分かりやすくないですか?
え、そんなんでもないですか。そうですか。はい。僕もそう思います。
※ 後述しますが、! ($i > $limit)
の部分が単純であれば話しは別です
さらに条件を入替えてみる
なんで、まだいまいち分かりづらいのかは先にも書いたとおり
- 条件が(複数)あってさらに否定がかまされている
この部分だと思います。
中の条件を理解したのに、それをひっくり返さなきゃいけない。
ここがつらみ。
自分的には、上で書いた 否定だけを取っ払ってみる の状態が(条件は無視で)一番分かりやすいです。
なので、否定の部分を反転してみます。
否定の部分を反転
for ($i = 0; $i <= $limit && $i < count($list); $i++) { }
どうでしょうか。これでやっと分かりやすくなったと思います。
! (条件) は、ぱっと見わかりづらい
今までの流れを書いてみると、
最初のコード
for ($i = 0; ! ($i > $limit || $i >= count($list)); $i++) { }
ド・モルガンの法則
for ($i = 0; ! ($i > $limit) && ! ($i >= count($list)); $i++) { }
否定の部分を反転
for ($i = 0; $i <= $limit && $i < count($list); $i++) { }
後者にいくほど分かりやすくなっていると思います。
こう見てみるとやっぱり自分的には、 ! (条件)
があるとぱっと見わかりづらくなるのではないかと考えます。
もちろん、頭に !
がついていても、その次の条件が単純な条件、例えば ! $i
とかであればそのままでいいと思います。
ですが、上にあるように、比較演算子や論理演算子などで繋がった条件の場合は、否定を反転したほうが個人的にはすっと条件が頭に入りやすい気がします。
まとめ
- 分かりづらい条件式は ド・モルガンの法則 を使うと分かりやすくなる
- ! (条件) は、ぱっと見わかりづらいので、否定を反転するとわかりやすくなるかも
ただし、なんでもかんでも入替え/反転するのでは無くて、その条件のほうが仕様に沿っているのであれば(その条件を言葉にしたらそのまま仕様になるみたいな)、わざわざ条件を変更しなくてもいいと思います。それで複雑な条件のままになってしまうのであればその部分を関数に抜き出すとかすればおkかと。
あと最後の最後でなんですけど今回の例で言えば、わざわざド・モルガンの法則とかを使わなくても、
for ($i = 0; ! ($i > $limit); $i++) { if ($i >= count($list)) { break; } // $list[$i]; を使って何かする処理 }
これでいいような気もします。
つまり結局のところ、読みやすい・理解しやすいコードを書こう!というところに収束するのだと思いますが、ちょっとこの条件分かりづらいかも?と思ったら、ド・モルガンの法則などを使ってみてはいかがでしょうか。
なんかセブンが見たくなった。