引数とデフォルト値を「キーワード引数」にするメリット

## はじめに

条件分岐の記述方に悩んでいたとき、
メソッドの引数に**デフォルト値**を設定できるということを
先輩に教えていただきました。

そしてデフォルト値に関する記事を読んでいる中、気になるワードをみつけました。

**「キーワード引数」**

これがとても勉強になったので、記録します。

（ちなみにチェリー本改定2版に、
デフォルト値もキーワード引数についてもちゃんと書いてありました😇
実際に使う場面にならないと覚えられないものですね…）

## そもそもデフォルト値とは

メソッドの引数に最初から設定できる値のことを指します。

```:構文
def メソッド名（引数名1 = デフォルト値1, 引数名2 = デフォルト値2 ...)
  処理の内容
end
```

メソッドを呼び出す時、
- 引数を渡さなければ、引数にデフォルト値が使われ、処理が行われます。
- 引数を渡せば、渡した引数が使われ、処理が行われます。
- 複数の引数を設定したときは、メソッドに近い（先頭の）引数から使われます。

```ruby:sample.rb
def eat(meat = 'beef') #'beef'をデフォルト値とする
  if meat == 'beef' # 引数とデフォルト値の値が等しければ真
    puts '牛肉は食べられます'
  else # 引数とデフォルト値の値が等しくなければ偽
    puts '牛肉以外は食べられません'
  end
end

# 引数に何の値も入れずメソッドを呼び出す
eat # => 牛肉は食べられます
# 引数に値を渡してメソッドを呼び出す
eat('chicken') # => 牛肉以外の肉は食べられません
```
~~高級志向メソッドが出来上がりました。~~


## デフォルト値を複数設定する
複数のデフォルト値を設定した場合は、
- 呼び出したメソッド側に記述した引数の先頭から順に、デフォルト値へ代入されていきます。
- 呼び出したメソッド側に記述した引数の方がデフォルト値より少なければ（引数の省略）、代入されなかった余りの引数には、デフォルト値が入ります。

```ruby:sample.rb
def cook_burger(meat = 'beef', vegetable = 'lettuce', topping = 'cheese')
  puts "ハンバーガーの具材は、#{meat}と#{vegetable}と#{topping}です。"
end

cook_burger('egg', 'pork')
# => ハンバーガーの具材は、eggとporkとcheeseです。
```

#### ちょっとまった

ちなみにこれ、本当は「てりたまバーガー」を作りたかったんですね🍔
レタスはそのままに、牛肉を豚肉に、チーズを卵に変えたかったのです。

期待した出力：`ハンバーガーの具材は、porkとlettuceとeggです。`
しかし、先ほど説明したように、
デフォルト値 > 呼び出しメソッドの引数だと、
呼び出しメソッドの引数の先頭順からデフォルト値に代入されてしまうため
出力結果：`ハンバーガーの具材は、eggとporkとcheeseです。`となりました。

また、引数に思いつくままの順番で材料を記述したことで、
デフォルト値が変更された後の引数は、こうなりました。
`(meat = 'egg', vegetable = 'pork', topping = 'cheese')`
引数名とデフォルト値の関係があやふやになり、これでは意味がありません。

:::note warn
順序に関係なく、対応する引数を決定したい
:::

## 意味がわかりづらい引数
一旦デフォルト値は置いておいて、
いつもどおり引数を設定したメソッドを見てみましょう。

```ruby:sample.rb
def buy_burger(main, drink, potato)
  puts "#{main}を注文します。"
  if drink
    puts 'セットに飲み物も注文します。'
  end
  if potato
    puts 'セットにポテトも注文します。'
  end
end

buy_burger('てりたま', true, true)
# => てりたまを注文します。
# => セットに飲み物も注文します。
# => セットにポテトも注文します。
```

#### ちょっとまった
`buy_burger('てりたま', true, true)`
この部分だけを見たとき、パッと見でこの引数が何を意味しているのか、よくわからなくありませんか？
（メソッド名から'てりたま'はなんとなく予想できるにしても、trueって何！？）

サンプルコードは定義したメソッドの近くでメソッドを呼び出しているので、
何が起きているか分かり易いと思いますが、
メソッドを呼び出すのが、メソッドが定義されたところよりももっと後だったら、どうでしょうか？

:::note warn
引数が何を意味するのか、わかりやすくしたい。
:::

## キーワード引数で解決！


・順序に関係なく、対応する引数を決定したい
・引数が何を意味するのか、わかりやすくしたい。


こんな要望に対して活躍するのが、**キーワード引数**です。

記述の仕方は、引数名の後には、`=`ではなく、`:`をつけます。

```:構文
▶︎ デフォルト値を設定する場合
def メソッド名（引数名1: デフォルト値1, 引数名2: デフォルト値2 ...)
  処理の内容
end

▶︎ デフォルト値を設定しない場合
def メソッド名（引数名1:, 引数名2: ...)
  処理の内容
end

※キーワード引数を持つメソッドを呼び出す場合、引数は、(引数名： 値)と記述してください
```

<br>
それでは、先ほどうまくいかなかったコードを書き換えてみます。

```ruby:sample.rb
def cook_burger(meat: 'beef', vegetable: 'lettuce', topping: 'cheese')
  puts "ハンバーガーの具材は、#{meat}と#{vegetable}と#{topping}です。"
end

# 期待する出力 => ハンバーガーの具材は、porkとlettuceとeggです。
cook_burger(topping: 'egg', meat: 'pork')
# => ハンバーガーの具材は、porkとlettuceとeggです。
```
今度こそ無事、てりたまの完成です🍔
キーワード引数があれば、順番を気にせずに対応する引数を決定できます。

もう一つの修正コードも見てみましょう。

```ruby:sample.rb
def buy_burger(main, drink: true, potato: true) 
  puts "#{main}を注文します。"
  if drink
    puts 'セットに飲み物も注文します。'
  end
  if potato
    puts 'セットにポテトも注文します。'
  end
end

buy_burger('てりたま', drink: true, potato: true)
# => てりたまを注文します。
# => セットに飲み物も注文します。
# => セットにポテトも注文します。
```
※引数の中は、引数とキーワード引数を混在させることが可能です。

`buy_burger('てりたま', drink: true, potato: true)`
引数の意図するところがわかりやすくなりました。

ちなみに、デフォルト値を与えないキーワード引数では、
メソッド呼び出しの際に、引数の省略ができません。

```ruby:sample.rb
def buy_burger(main, drink:, potato:) 
  puts "#{main}を注文します。"
  if drink # 省略
  end
  if potato # 省略
  end

# `potato:`にデフォルト値を設定していないのに、引数の記述を省略した
buy_burgerbuy_burger('てりたま', drink: true)
# => `buy_burger': missing keyword: :potato (ArgumentError)
```
この動作を用いることで、キーワード引数を必須化することもできます。


## キーワード引数のメリット

:::note info
引数にキーワードを付けることで、

引数に指定する順序を考慮する必要がなくなる
何を意味するものか分かりやすくなり、コードの可読性もあがる。
:::

## あとがき

引数ひとつにしても本当に奥深いな〜と勉強になりました！
コードの可読性をあげることは、開発において大事だと思うので、
デフォルト値設定の時は、キーワード引数を使うよう、心がけたいと思います！
（時と場合によるかもしれませんが）

<font color="DarkGray">記事の内容に誤りがありましたら、コメントで教えていただければ幸いです…！</font>

#### 参考

- [Rubyのメソッドの引数でデフォルト式を与える場合は、通常の引数ではなく「キーワード引数」を使おう](https://qiita.com/aeroastro/items/49e18cb7e95a64e0eacf)
- [Rubyのキーワード引数](https://tokitsubaki.com/ruby-keyword-arguments/531/)

#### キーワード引数が、ハッシュに似てるなって思った人へ
- [Rubyのキーワード引数はシンボルっぽく定義するけど、シンボルそのものではない、という話](https://qiita.com/jnchito/items/74e0930c54df90f9704c)