Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails関連

Rubyにおけるunlessとコードの読みやすさについて

更新情報

  • 2016/08/23: 初版公開
  • 2022/11/17: 細部を更新

こんにちは、hachi8833です。

状況や好みによって異なりますが、条件分岐が二重否定の形になると、一般にコードが読みにくくなる傾向があります。

160821_0818_5HNrsp

unlessはうまく使えば読みやすいコードを書くときに役立ちますが、unlessが二重否定を形成するとかえって読みにくくなることもあります。三重四重は言わずもがなですね。

🔗 unless||の組み合わせは避けよう

unlessの是非は好みが分かれることが多く、よく議論のネタになります。

少なくともunless||の組み合わせは苦情が出やすいので避けましょう。

 unless obj1.blank? || obj2.blank? 

言葉で書けば「obj1とobj2のどちらもblankでないなら」となります。コーディング前にこのような表現で考えるのはよくあることですが、それをそのままロジックに落としこむと上のようなコードになってしまいます。

blank?と逆のpresent?が使えるなら、ド・モルガンの法則(後述)を活用して次のように書き換えると、ぐっと目に優しいコードになります。

if obj1.present? && obj2.present? 

言葉で書けば「obj1とobj2が両方とも存在すれば」となり、こちらも素直な表現になります。

🔗 not演算子!の混用は避けよう

unless条件の中でnot演算子!が使われると、二重否定が発生して読みにくくなるので、避けましょう。

unless !obj1.blank? #=> 読みにくい

if obj1.blank?      #=> 読みやすい

unlessに限らず、複数条件の一部でnot演算子!が使われると「部分否定」の形になり、読みにくくなります。

blank?present?のように、対義語メソッドに置き換えるなどして、not演算子!を含まない表現にすることで、読みやすくなります。

if obj1.blank? && !obj2.blank?     #=> 読みにくい

unless obj1.blank? && !obj2.blank? #=> さらに読みにくい

if obj1.blank? && obj2.present?    #=> 比較的読みやすい

🔗 参考: ド・モルガンの法則

任意の命題P,Q\in {\bot ,\top }に対して

    \[\neg (P\lor Q)=\neg P\land \neg Q\]

    \[\neg (P\land Q)=\neg P\lor \neg Q\]

が成り立つ。これをド・モルガンの法則という。
ド・モルガンの法則 – Wikipediaより

同じことをC言語のようなプログラミング言語の記号で表せば、P, Q がどんな論理式であろうと以下が成り立つということです。

!(P || Q) == !P && !Q

!(P && Q) == !P || !Q

「命題の対偶は常に真である」と並んで、安心して使えるロジックですね。

🔗 unlessはシンプルな場合に使おう

条件が1つで、かつelse文を使わないシンプルな条件判断であれば、unlessを使うことで視認性が向上することもあります。

unless obj1.member?
  何かする
end

特に実行するコードが1行だけの場合は、後置のunlessにするのがRuboCopでも推奨されています。

Club.alert unless obj1.member?

参考: Style/IfUnlessModifier — Style :: RuboCop Docs

🔗 unless A && Bはどうか

unless A && Bはまだ許せるけどunless A || Bは混乱するから嫌だ」という意見が社内でいくつか出ました。

unless A || Bの場合、A || Bが成立するパターンが4つのうち3つもあり(Aだけがtrueの場合、Bだけがtrueの場合、AとBが両方ともtrueの場合)、さらにそれぞれが否定されるので、脳への負担が大きくなりそうです。

unless A && Bの場合、A && Bが成立するパターンが4つのうちで1つしかない(=AとBがtrueの場合に限る)ので、その分ましなのでしょう。

それでもunlessの複数条件は読みにくくなりやすいので、可能であればド・モルガンの法則を使ってifに書き換えることを検討してみましょう。

🔗 「場合による」という意見

160823_1522_zOlkdO

対義語メソッドについては別途記事にする予定です。

おまけ

160821_0922_mFmxlb

関連記事

Railsでnil? blank? empty? present?を使いこなそう

Railsでbefore_filter/before_actionがアクションを中止する仕組みを読んでみる


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。