すでにメンバーの場合は

無料会員登録

GitHubアカウントで登録 Pikawakaが許可なくTwitterやFacebookに投稿することはありません。

登録がまだの方はこちらから

Pikawakaにログイン

GitHubアカウントでログイン Pikawakaが許可なくTwitterやFacebookに投稿することはありません。

Ruby

【Ruby】 unlessの使い方を正しく学んで可読性を下げないようにしよう

ぴっかちゃん
ぴっかちゃん

unlessとは、条件式の判定によって処理が分岐される制御構造のひとつです。条件式を評価した結果が偽の場合にthen以下に記述する(式を評価)処理が実行されます。

unlessは、unlessのあとに記述する条件式が偽の場合にthen~endまでの処理を実行します。

unlessの文法
1
2
3
unless 条件式 [then]
  # 条件式が偽の時に実行する処理
end

例えば、次のコードではbreakfastに'パン'が代入されているので、条件式のbreakfast == 'ご飯'は偽になり、ご飯ではありません。が出力されます。

breakfast.rb | unlessのサンプルコード
1
2
3
4
5
6
breakfast = 'パン'
unless breakfast == 'ご飯' then puts 'ご飯ではありません。' end #=>ご飯ではありません。

この記事では、unlessの使い方や処理の流れ、可読性における注意点など学ぶことができます。1つ1つ整理しながら理解を深めていきましょう。

unlessの使い方

この章では、unlessの書き方や処理の流れ、返り値など基本的な部分について解説します。

基本構文

unlessは、次のようにunless~endまでが範囲になります。

unlessの文法
1
2
3
unless 条件式 [then]
  # 条件式が偽の時に実行する処理
end
  • unless - 条件式を記述します。
  • then以下 - 条件式が偽の場合に実行する処理を記述します。

unless 条件式 thenとは「条件式を評価した結果が偽の場合にthen以下の式を評価する」という意味になります。

また、次のようにunlessはelseを使うことができます。

unlessの文法(elseを使用する場合)
1
2
3
4
5
unless 条件式 [then]
  # 条件式が偽の時に実行する処理
else
  # 条件式が真の時に実行する処理
end

可読性における注意点」の章で詳しく解説しますが、可読性の問題やifで事足りることからunlessでelseが使われることは多くありません。

※thenは省略することができます。(この章以降はthenを省略します。)

処理の流れ

記事の冒頭では、unlessについて下記のように記述しました。

unlessの後に記述する条件式を評価した結果が偽の場合は、then以下に記述する式を評価します。

この処理の流れをサンプルコードで表すと、次のような順番になります。

unlessの処理の流れ

  1. breakfast == 'ご飯'の条件式が評価される。(偽という評価結果になる)
  2. then以下の式が評価されて、ご飯ではありません。が出力される。

上記の「条件式が評価される。」とありますが、次の動画のように式(breakfast == 'ご飯')を実行して値を得ることを「評価」といい、値(false)が評価の結果になります。※Rubyではfalseまたはnilが偽です。

式を評価した結果falseが返る例

つまり、unlessは条件式の評価結果がfalseならば、then以下の式を評価します。

ポイント
  1. 式を実行して値を得ることを「評価」という
  2. 上記の値は「評価の結果」という

返り値

unlessの返り値は、条件が成立しなければthen以下の最後に評価した式の結果を返し、条件が成立すればnilを返します。

例えば、次のように条件式の評価結果が偽だと式の評価結果("条件が不成立")を返し、条件式の評価結果が真だとnilを返します。

irb | 条件が不成立の場合
1
2
3
4
irb(main):001:0> unless 1 == 10
irb(main):002:1> "条件が不成立" # 条件式が偽なので、この式が評価される
irb(main):002:1> end
=> "条件が不成立"
irb | 条件が成立する場合
1
2
3
4
irb(main):001:0> unless 1 == 1
irb(main):002:1> "条件が成立" # 条件式が真なので、この式は評価されない
irb(main):002:1> end
=> nil

先ほどの補足説明で少し触れましたがunlessは値を返す式なので、次のように変数に代入して使用することもできます。

unless式を代入した場合

ポイント
  1. unlessの条件式が偽の場合は、(then以下の)最後に評価された式の結果が返り値になる
  2. unlessの条件式が真の場合は、nilが返り値になる

後置unless(unless修飾子)

unlessは後置unless(unless修飾子)を使用すると、1行で記述することができます。

unless修飾子の文法
1
2
 unless 
# [条件式が偽の時に実行する処理] unless [条件式]

後置unlessでは、右辺の条件が成立しない(偽になる)場合に左辺の式を評価し、その評価結果を返します。

例えば、これまでの解説に使用していたサンプルコードを後置unlessにすると、次のように記述することが出来ます。

breakfast.rb | unlessのサンプルコード
1
2
3
4
5
6
7
8
9
breakfast = 'パン'

# 後置unlessを使用しない場合
unless breakfast == 'ご飯'
puts 'ご飯ではありません。'
end
# 後置unlessを使用した場合
puts 'ご飯ではありません。' unless breakfast == 'ご飯'

このように後置unlessは、1行に記述するコードが短い場合に使用することで、簡潔に記述することができます。

しかし、1行に記述するコードが長い場合に後置unlessを使用すると可読性が下がります。
例えば、次のコードはどうでしょうか?

1
puts '今日の朝食はご飯ではありません。しかし、明日の朝食はご飯にしようか検討中です。他にも候補はあります。' unless breakfast == 'ご飯'

コードを右端まで読んではじめてbreakfast == 'ご飯'ではないときにputs ~の処理を実行することがわかりますね。1行に記述するコードが長くなる場合は後置unlessを使用しない方が読みやすくなります。

1
2
3
unless breakfast == 'ご飯'
  puts '今日の朝食はご飯ではありません。しかし、明日の朝食はご飯にしようか検討中です。他にも候補はあります。'
end

コードの記述量が減るので後置unlessに置き換えた方が良さそうと思われがちですが、下手すると可読性が下がり読みづらいコードになってしまうので使用する際は注意が必要です。

ポイント
  1. 全て後置unlessに置き換える必要はない
  2. 後置unlessにした方がシンプルで可読性が良くなると思える場合のみ使用する

可読性における注意点

unlessはいくつかの状況に使うと、混乱するようなかえって読みにくいコードになります。

この章では特に可読性に関わる以下の注意点について解説します。

  • unlessとelseの併用を避けよう
  • unlessとnot演算子の併用を避けよう
  • 対義語メソッドで肯定にできるか検討しよう
  • unlessと||の併用を避けよう

unlessとelseの併用を避けよう

次のようにunless~elseif~elseに置き換えることができるので、頻繁に使われるものではありません。

unlessでelseを使用する場合
1
2
3
4
5
unless true
  # 条件式が偽の時に実行する
else
  # 条件式が真の時に実行する
end
unlessをifに置き換えた場合
1
2
3
4
5
if true
  # 条件式が真の時に実行する
else
  # 条件式が偽の時に実行する
end

またunless~elseelseの箇所で二重否定(trueでなくない)になり、読みづらいコードが出来上がってしまうので避けるようにしましょう。

以下はunless~elseif~elseに置き換えたコードの例です。

unless~elseをif~elseに置き換える場合
1
2
3
4
5
6
7
8
9
10
11
12
13
unless number.even?
  "偶数ではありません"
else
  "偶数です。"
end

# 上記のunlessは以下のifに置き換えられる

if !number.even?
  "偶数ではありません"
else
  "偶数です。"
end

ifの条件式にnot演算子!を指定することで、同じ意味で記述することができています。

unlessとnot演算子の併用を避けよう

先ほどのifの条件式にはnot演算子!を使っていましたが、unlessの条件式にnot演算子を使うと二重否定になってしまうので使用を避けるようにしてください。

次のunless !trueは、「trueでなくなければ~」という二重否定になります。

1
2
3
unless !true
  # 条件式が真の時に実行する
end

上記はtrueの場合に実行されたら良いので、次のようにifに置き換えることができます。

1
2
3
if true
  # 条件式が真の時に実行する
end

例えば、unless!number.even?は二重否定で「numberが偶数でなくないならば~」と理解しづらいですが、結局「numberが偶数ならば~」を意味しているので、次のように置き換えることができます。

unlessのnot演算子をifに置き換える場合
1
2
3
4
5
6
7
8
9
unless !number.even?
 puts "偶数です。"
end

# 上記のunlessは以下のifに置き換えられる

if number.even?
  "偶数です。"
end

このようにnot演算子をunlessの条件式に使うと、コードが読みづらくなってしまうので注意が必要です。

対義語メソッドで肯定にできるか検討しよう

unlessの条件式でメソッドを使うときは、場合によっては対義語メソッドでifに置き換えることができます。

次のコードはeven?メソッドの対義語であるodd?メソッドを使って置き換えています。

対義語メソッドで肯定にする
1
2
3
4
5
puts "奇数です。" unless number.even?

# 上記のunlessは以下のifに置き換えられる

puts "奇数です。" if number.odd?

「numberが偶数ではければ」から「numberが奇数ならば」と肯定の形になるので、否定よりも読みやすくなりますね。

また、blank?メソッドを使う場合は、present?メソッドを使うことでifに置き換えることができます。(どちらもActiveSupportが提供するメソッドです。)

対義語メソッドで肯定にする
1
2
3
4
5
puts "空ではありません。" unless number.blank?

# 上記のunlessは以下のifに置き換えられる

puts "空ではありません。" if number.present?

こちらも「numberが空でなければ」から「numberが存在すれば」と肯定の形になるので、読みやすくなります。

注意点

ifよりもunlessの方が自然な表現で直感的なコードになる場合は、unlessを使いましょう。 例えば、次のコードは「ユーザーが存在しなければ、サービスを利用できない」と文脈で考えても意味が通りやすくコードの可読性も良いですね。

unlessを使用した場合
1
'サービスは利用できません。' unless User.exists?(id: user_id)

しかし、次のコードは「ユーザを検索した結果が空であれば、サービスを利用できない」という文脈になってしまい直感的ではありません。

ifを使用した場合
1
'サービスは利用できません。' if User.where(id: user_id).blank?
このように状況によって使い分けるようにしましょう。上記で使用したメソッドの詳細は、whereメソッドexists?メソッドを参考にしてください。

unlessと||の併用を避けよう

条件分岐では||&&などの「論理演算子」を使って条件を組み合わせることができますが、unlessで||演算子を使うとコードが読みづらくなる傾向があります。

||演算子」とは、次のように左から右へ順番に式を評価し、結果が真の場合にその値を返します。結果が偽の場合は、右の式を評価しその結果を返します。

|| 演算子の例
1
2
3
nil || false || 10 || true
#(偽)|| (偽) || (真) || (真)
=> 10

unlessに||を使うと、次のように条件を組み合わせることができます。

unlessに ||演算子を使用した場合
1
2
3
4
number = 3

"1と2ではない" unless number == 1 || number == 2 
#=> "1と2ではない"

しかし、これは以下の2つの理由から読みづらいコードとなります。

  1. A || Bの成立するパターンが3つあるため
  2. さらにそれをunlessで否定するため

そもそもA || B(number == 1 || number == 2)は、次の表のように成立するパターンが3つあります。

A || Bが成立するパターン 式全体の結果
A=true, B=true(両方がtrue) true
A=true, B=false(片方がtrue) true
A=false, B=true(片方がtrue) true

そしてunlessで否定するので、読みづらく混乱するコードになってしまうのです。

ド・モルガンの法則で置き換える

ド・モルガンの法則を使えばifに置き換えて理解しやすいコードにすることができます。

ド・モルガンの法則は、集合論の定理の1つで「ある見方をすればand(&&)演算子とor(||)演算子はお互いに変換することができる」というものです。

例えばunless A || Bは次のように表すことができます。

unless A || Bの表示

これをド・モルガンの法則を使うと、次のように変換することができます。

 if !A && !Bの表示

この変換によって「Aがtrueではなく、Bもtrueではなければ」から「AがfalseかつBもfalseなら」と自然な表現にすることができますね。

この法則に従って先ほどのコードも次のように変換することができます。

unlessと||の組み合わせをifと!に変換する
1
2
3
4
5
"1と2ではない" unless number == 1 || number == 2 

# 上記はド・モルガンの法則によって以下に変換できる

"1と2ではない" if !(number == 1) && !(number == 2)

A || Bのときは成立パターンを3つ考えなくては行けませんでしたが、A && Bは次の表のように1つだけで良いので理解しやすくなります。

A && Bが成立するパターン 式全体の結果
A=true, B=true(両方がtrue) true

今回は!A && !BなのでAとBのどちらもfalseの場合に式全体がtrueになります。この1つのパターンだけ考えれば良いので、||と比べて読むのが楽になりますね。

ポイント

unlessと||の組み合わせは混乱しやすいコードになるので、ド・モルガンの法則を使って置き換えれないか考えよう

制御構造の条件分岐まとめ

Rubyの条件分岐は、次の表の通りです。

条件分岐 説明 リンク
case 1つの式に対する一致判定によって処理が分岐される caseの使い方
if 真偽値で条件を分岐することができる
(真の時にthen以下の式を評価)
ifの使い方
unless 真偽値で条件を分岐することができる
(偽の時にthen以下の式を評価)

この記事のまとめ

  • unlessとは、条件式の判定によって処理が分岐される制御構造のひとつ
  • 条件式を評価した結果が偽の時に処理が実行される
  • unlessを使うとコードの可読性が下がる場合があるので注意が必要!