【Ruby】真偽値と論理演算子あれこれ

#はじめに
Rubyの真偽値や論理演算子の見落としがちなポイントについて軽くまとめてみました。

ぜひ最後までご覧ください。

#対象読者
- Rubyを学び始めた人
- プログラミング始めたての人
- 真偽値や論理演算子を用いた式の計算がわからない人

#本記事の内容
[1. Rubyの真偽値について](#1-Rubyの真偽値について)
[2. 条件分岐（ifとunless）で忘れがちなポイント](#2-条件分岐（ifとunless）で忘れがちなポイント)
[3. 論理演算子を含む式の戻り値](#3-論理演算子を含む式の戻り値)
[4. まとめ](#4-まとめ)
[5. 最後に](#5-最後に)
[6. 参照サイト](#6-参照サイト)


#1. Rubyの真偽値について
Rubyの真偽値は以下のようなルールを持っています。

- <code>falseとnilは**「偽」**と判定
- <code>それ以外の値は全て**「真」**と判定

```ruby
# 全ての値とtrueそのものは真
1
2
3
[1, 2, 3]
'apple'
''
true

# falseとnilは偽
false
nil
```
そして、<code>これらの性質を生かして実際のコードを書く場面は往々にしてあります。

```ruby
# 真偽値のルールを意識せずに書いた場合
if foo == true
  # ...
end

if bar == false
  # ...
end

if baz == nil
  # ...
end

# 真偽値のルールを意識して書いた場合
if foo
  # ...
end

unless bar
  # ...
end

unless baz
  # ...
end
```

<code>当然後者のコードの方がリファクタリングされており、綺麗なコードとみなされます。

#2. 条件分岐（ifとunless）の忘れがちなポイント
次に、条件分岐に関する文を作る時に、忘れがちなポイントについて説明していきます。

## 戻り値は最後に評価された式の値
まず一つ目は、<code>**戻り値**</code>についてです。

Rubyにおいて、**if文やunless文は最後に評価された式の値を戻り値として返します。**

これだけだとどういうことか、わかりづらいと思いますので、実際のコードをみながら考えていきましょう。

以下のコードを<code>irbで実行する場合を考えてみます。

```ruby
# 戻り値を意識せずに書いた場合
num = 2

if num.odd? 
  puts '奇数です'
else 
  puts '偶数です'
end
#=> 偶数です

# 戻り値を意識して書いた場合
num = 2

if num.odd?
  '奇数です'
else
# if文はこの下式の値を戻り値として返す
  '偶数です'
end
#=> 偶数です
```
後者の場合、numは奇数ではないため、最初の条件には当てはまらず、二番目の条件に合致します。

すると、**その合致した条件下の処理内容に含まれる式の値をif文全体の戻り値として返します。**

つまり、irbなどで実行する場合は<code>前者のように**puts**を使わなくてもif文の戻り値を見ることによって、どの条件が実行されたのか確認することができるのです。

## 後置if
次に忘れがちなポイントとしては**<code>後置if</code>**があります。

後置ifとは、その名の通り**本来文の先頭に置くif文を文の後ろに置くこと**です。

```ruby
# 後置ifを用いない場合
# (nil?はオブジェクトがnilならばtrueを返すメソッド)
if user.nil?
  redeirect_to :root
end

# 後置ifを用いた場合
redirect_to :root if user.nil?
```
こうすることで行数を減らすことが可能になり、**シンプルにコードを記述することができます。**

ただし、注意点としては後置ifは評価した結果を戻り値として返そうとすると、**<code>想定していた結果と違ったもの</code>が返ってくるということが往々にしてあります。**

```ruby
def greeting(country)
  # countryに指定がなければ、"Hello"と返したい
  'Hello'
  # countryが'japan'ならば、"こんにちは"と返したい
  'こんにちは' if country == 'japan'
end
greeting('japan') #=> "こんにちは"
greeting('us') #=> nil
```

これは、**後置ifの条件文がfalseだった場合、ifの前の処理<code>（ここでは'こんにちは'）</code>を評価せずにnilを返すという仕組みになっているからです。**

そのため、後置ifは無闇に使うのではなく

- **後述の早期returnと合わせて用いる**
- **式ではなく、文として取り扱う（戻り値を使って処理を書かない）**

というような場合に用いる方がベターで、安全とされています。

## 早期returnを用いる
条件分岐文では、<code>条件を満たした場合にすぐにreturnさせて後ろに影響を及ぼさないようにするという手法がよく取られます。

これを<code>**早期return**</code>と呼び、**あまり起きないケースや、条件文のネストを深くするといったことを避けるためによく用いられます。**

```ruby
def greeting(country)
  # countryがnilならメッセージを返してメソッドを抜ける
  return 'countryを入力してください' if country.nil?
  
  # countryが'japan'なら'こんにちは'と返してメソッドを抜ける
  return 'こんにちは' if country == 'japan'
  # それ以外なら'hello'と返す
  'Hello'
end
greeting(nil)     #=> "countryを入力してください"
greeting('japan') #=> "こんにちは"
greeting('us')    #=> "Hello"
```



#3. 論理演算子を含む式の戻り値
最後に論理演算子を用いた式で、見落としがちなポイントについて説明していきます。

## &&や||の戻り値と評価の終了するタイミング
論理演算子&&や||を用いた場合の式の戻り値については注意が必要です。

以下の結果をご覧ください。

```ruby
# 例1
'apple' && 'banana' && 'grape' #=> "grape" 
# 例２
'apple' && nil && 'grape'      #=> nil
'apple' && false && 'grape'    #=> false

# 例3
nil || false                        #=> false
false || nil                        #=> nil
nil || false || 'apple' || 'banana' #=> 'apple'
```
このように必ずしも、**trueやfalse**にはなりません。

**これはRubyは式全体が真または偽であることが決定するまで左辺から順に式を評価していくことが関連しています。**

そして<code>式全体の真または偽が確定したタイミングで式の評価を終了し、最後に評価した式の値を返すという仕組みになっています。

そのため

- **例1の場合**

全ての式を評価する必要があったため、最後の式の**<code>'grape'</code>**を戻り値として返します

- **例2の場合**

<code>2つ目のnilを評価した時点</code>で式全体の真偽値が<code>偽</code>であることが決まったため、その時点で評価を終了し、**<code>最後のnil</code>**を返します。falseの場合も同様です

- **例3の場合**

||の場合も考え方は同じで、例えば1行目の場合、最後の時点で式全体の真偽値が確定したため、最後の式の値である**<code>false</code>**を戻り値として返す。2行目も同様です。

また3行目の場合、戻り値が'apple'となるのは、<code>'apple'の式まできた時に式全体の真偽値が決定したためです。</code>
<br>
***
さらに注意すべき点としてはif文の場合はそれほど意識しなくていいですが、**if文以外の場面で<code>&&や||を意図的に用いる場合</code>もあるということです。**

そのため、これらの論理演算子を見かけたら、上記のような使われ方がしていないか、注意し見てみましょう。

## andとorを用いた制御フロー
最後に上の&&、||と同じような働きをする演算子としてand、orがありますが、そちらの使われ方についても軽く触れておきます。

andとorは基本的には&&と||と同じですが、<code>いくつかの場面において違った結果を返すことがあります。

これは、**演算子の優先順位**が関係しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/751055/5cc17a3d-c069-98bd-4bc3-a6095233dd26.png)

上記の図のようになっており、andとorは**<code>&&と||</code>よりも優先順位が低いという状態になっています。**

例えば、以下のようなコードを書いたとします。

```ruby
t1 = true
t2 = false
f1 = false

t1 || t2 && f1 #=> true
t1 or t2 and f1 #=> false
```

この場合、これは次のコードを書いたことと同じことになります。

```ruby
# &&は||よりも優先順位が高い
t1 || (t2 && f1)
# andとorの優先順位は同じなので、左から順に評価される
(t1 or t2) and f1
```

これらからも、<code>andとorが&&と||とは完全に同じようには扱えないということがわかります。

そしてさらにこのような特徴を持っているandとorは条件分岐ではあまり用いられず、**制御フローを扱うことに向いています。**

```ruby
def greeting(country)
  # countryがnilまたはfalseならメッセージを返してメソッドを抜ける
  country or return 'countryを入力してください'

  return 'こんにちは' if country == 'japan'
  'hello'
end

greeting(nil)     #=> "countryを入力してください" 
greeting('japan') #=> "こんにちは"
greeting('us')    #=> "hello"
```
上のコードは<code>**「Aが真か？　真でなければBせよ」という制御フロー**</code>を実現していて、このような場合にandとorは用いられます。。

#4. まとめ

- Rubyの真偽値は<code>falseとnilは**「偽」**でそれ以外の値は全て**「真」**と判定する

- Rubyにおいてif文の戻り値は<code>**最後に評価された式の値**</code>
- 後置if文を用いることで<code>**シンプルなコードがかける**</code>
- 早期returnは<code>**あまり起きないケースや条件文のネストが深くなってしまうこと**</code>を避けるために用いられる。
- Rubyでは**式全体が真または偽であることが決定するまで、左辺から順に式を評価**していき、**<code>式全体の真偽値が確定したタイミングで式の評価を終了して、最後に評価した式の値を戻り値として返す**
- andとorは&&と||よりも**<code>優先順位が低く、制御フローでよく用いられる**

#5. 最後に
本記事の内容がみなさんの参考になれば嬉しいです。

最後までご覧いただきありがとうございました。

#6. 参考文献
[Rubyリファレンスマニュアル: 制御構造](https://docs.ruby-lang.org/ja/latest/doc/spec=2fcontrol.html)
[【Ruby】後置ifが末尾にあるメソッドの返り値はなに...？](https://ysk-pro.hatenablog.com/entry/if-return-value)
[Rubyのメソッドの返り値にreturnを使うのは邪道なんですか ](https://github.com/kure-kosen/QnA-Issues/issues/4)
[Rubyの「後置if」は「ifの評価」が戻り値](https://bottoms-programming.com/archives/ruby-modifier-if.html)
[rubyで早期return](https://qiita.com/n_slender/items/e062454114facd00a942)
[Rubyでreturnを使って条件分岐をシンプルに書く方法](https://qiita.com/okhrt/items/3e98ba496bcf8c50ac64)
[[初心者向け] RubyやRailsでリファクタリングに使えそうなイディオムとか便利メソッドとか](https://qiita.com/jnchito/items/dedb3b889ab226933ccf#%E4%BB%A3%E5%85%A5%E3%81%97%E3%81%A6%E3%81%8B%E3%82%89if%E3%81%A7%E5%AD%98%E5%9C%A8%E3%82%92%E7%A2%BA%E8%AA%8D%E3%82%92%E3%81%BE%E3%81%A8%E3%82%81%E3%81%A6%E6%9B%B8%E3%81%8F)
[書籍：プロを目指す人のためのRuby入門](https://www.amazon.co.jp/dp/B077Q8BXHC/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)