【翻訳&学習】Rubocopのクラス`Class: RuboCop::Cop::Style::EachWithObject`について

## 要約
[Rubocop](https://github.com/rubocop-hq/rubocop)のクラス`Class: RuboCop::Cop::Style::EachWithObject`について下記サイトのページを翻訳しました。(2019/02/24現在)  

- [Class: RuboCop::Cop::Style::EachWithObject — Documentation for rubocop (0.65.0)](https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Style/EachWithObject)

概ね直訳になるように心掛けましたが、どうしても直訳だと分かりづらい箇所は、分かりやすく翻訳しています。
記事を読んでもコード例を見ても詳しいところまでしっくり来づらかったので、その辺りの詳細を調べました。

記事の対象は、以下のような初心者の方です。

- 上記のクラスについて知りたい方
- Rubocopのドキュメントの読み方に慣れたい方
- [`inject`メソッド](https://ref.xaio.jp/ruby/classes/enumerable/inject)の使いどころが分からない方

## 翻訳

|親クラス|[Cop](https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Cop)|
|:---------|:---------|
|インクルードしているモジュール|[RangeHelp](https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/RangeHelp)|
|定義されているファイル|lib/rubocop/cop/style/each_with_object.rb|

### 概略(Overview)
この警官(cop)は、`inject`メソッドまたは`reduce`メソッドが使用されている箇所のブロックに、(訳注: メソッドの引数として)渡されたオブジェクトがそのままブロックの最後で返却(return)されているため、そのオブジェクトをブロックの最後で返却する必要のない`each_with_object`メソッドに置き換えられるかもしれないような箇所を探す。

しかしながら、アキュムレータのパラメータにブロック内で(訳注: 別のオブジェクトが)代入されている場合は、`each_with_object`メソッドに置き換えることができない。

#### 例(Examples):
```
# 悪い例
[1, 2].inject({}) { |a, e| a[e] = e; a }

# よい例
[1, 2].each_with_object({}) { |e, a| a[e] = e }
```

### 定数一覧(Constant Summary)
- MSG =  
    'Use `each_with_object` instead of `%<method>s`.'.freeze  
    (訳注: 上記の定数の内容を日本語に訳すと、「`%<method>s`の代わりに、`each_with_object`を使ってください」という意味になります。)
- METHODS =  
    %i[inject reduce].freeze
- [Utilモジュール](https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Util)からインクルードされた定数一覧  
    [Util::LITERAL_REGEX](https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Util#LITERAL_REGEX-constant)

### インスタンス属性一覧(Instance Attribute Summary)
- [Copクラス](https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Cop)から継承した属性一覧  
#config, #corrections, #offenses, #processed_source

### インスタンスメソッド一覧(Instance Method Summary)
(メソッド数が多い上に翻訳すべき箇所も少ないため、当項目は省略します。)  

### コンストラクタの詳細(Constructor Details)
このクラスは、[RuboCop::Cop::Copクラス](https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Cop#initialize-instance_method)からコンストラクタ(訳注: `initialize`メソッドのこと)を継承している。

### インスタンスメソッドの詳細(Instance Method Details)
(翻訳すべき箇所が少ないため、当項目は省略します。)  

---
翻訳は以上です。

## 解説
要約(Overview)部分の説明が分かりづらいと思うので、この部分について解説します。

### 登場する各メソッドについて
各メソッドの説明については、下記サイトが分かりやすいです。

- [`inject`メソッド・`reduce`メソッド](https://docs.ruby-lang.org/ja/latest/method/Enumerable/i/inject.html)
- [`each_with_object`メソッド](https://docs.ruby-lang.org/ja/latest/method/Enumerable/i/each_with_object.html)

`reduce`メソッドは`inject`メソッドの別名なので、これ以降は`inject`メソッドに絞って説明します。

### 概略(Overview)の説明  
やはり、概略(Overview)で書かれていることが分かりづらいので、この辺りを噛み砕いていきます。

#### *`inject`メソッドまたは`reduce`メソッドが使用されている箇所のブロックに、(訳注: メソッドの引数として)渡されたオブジェクト*
このオブジェクトとは、以下のコードでいうところの`inject`メソッドの引数`[]`のことを指しています。

```ruby:not_good_inject.rb
a = [1, 2, 3, 4].inject([]) do |sum, n|
  sum << n if (n % 2).zero?
  sum
end

p a
```
`inject`メソッドの戻り値は、 **ブロックが最後に返した値** です。
上記コードの出力結果は、`[2,4]`です。以下のような処理を行っています。

- 「nの値が偶数の時、ブロックの戻り値の配列の中にnを要素として追加する」  
下記は詳細な処理内容です。
  1. メソッドの引数に[]を渡す(メソッドの引数は初期値として使用される)
  1. 初期値([])と要素1(1)をブロックに渡す
  1. 1は奇数なので`sum`に要素は追加されず、ブロックの最後でsum([])が返却される
  1. ブロックの前回の戻り値([])と要素2(2)をブロックに渡す
  1. 2は偶数なので`sum`に要素が追加され、ブロックの最後でsum([2])が返却される
  1. ブロックの前回の戻り値([2])と要素3(3)をブロックに渡す
  1. 3は奇数なので`sum`に要素は追加されず、ブロックの最後でsum([2])が返却される
  1. ブロックの前回の戻り値([2])と要素4(4)をブロックに渡す
  1. 4は偶数なので`sum`に要素が追加され、ブロックの最後でsum([2, 4])が返却される
  1. **ブロックが最後に返した値** `[2,4]`がメソッドの戻り値となる


#### *そのオブジェクトをブロックの最後で返却する必要のない`each_with_object`メソッドに置き換えられるかもしれない*  
一方、`each_with_object`メソッドの戻り値は、 **メソッドの引数として渡されたオブジェクト** です。  
以下の例を見てください。

```ruby:each_with_object.rb
# コード2
b = [1, 2, 3, 4].each_with_object([]) { |n, arr| arr << n if (n % 2).zero? }

p b
```
上記コードの出力結果は、`[2,4]`です。以下のような処理を行っています。

- 「nの値が偶数の時、メソッドの引数の配列の中にnを要素として追加する」  
下記は詳細な処理内容です。
  1. メソッドの引数に[]を渡す
  1. 要素1(1)と引数のオブジェクト([])をブロックに渡す
  1. 1は奇数なので`arr`(引数のオブジェクト)に要素は追加されない
  1. 要素2(2)と引数のオブジェクト([])をブロックに渡す
  1. 2は偶数なので`arr`(引数のオブジェクト)に要素が追加される
  1. 要素3(3)と引数のオブジェクト([2])をブロックに渡す
  1. 3は奇数なので`arr`(引数のオブジェクト)に要素は追加されない
  1. 要素4(4)と引数のオブジェクト([])をブロックに渡す
  1. 4は偶数なので`arr`(引数のオブジェクト)に要素が追加される
  1. メソッドの引数の **オブジェクト** `[2,4]`がメソッドの戻り値となる

確かに、`not_good_inject.rb`のソースは、`each_with_object.rb`で置き換えることができます。
しかも、`inject`メソッドの代わりに`each_with_object`メソッドを使うことで、わざわざブロックの最後でメソッドの引数のオブジェクトを返却する必要がなくなっています。
Rubocopは、上記のような`inject`メソッドの使用について警告してくれるのです。

#### *アキュムレータのパラメータにブロック内で(訳注: 別のオブジェクトが)代入されている場合は、`each_with_object`メソッドに置き換えることができない*  
**アキュムレータのパラメータ** とは、上記コード`not_good_inject.rb`で言うところの、`sum`(繰り返し計算で累積された値を保持している変数)のことです。  
上記の説明に該当するのは、以下のようなコードです。

```ruby:bad_inject.rb
a = [1, 2, 3, 4].inject([]) do |sum, n|
  sum << n if (n % 2).zero?
  sum = 1
  sum
end

p a
```

上記コードでは、`sum`に1を代入しています。出力結果は`1`です。

それでは、上記コードを単純に`each_with_object`に置き換えるとどうなるでしょうか。

```ruby:bad_each_with_object.rb
a = [1, 2, 3, 4].each_with_object([]) do |n, sum|
  sum << n if (n % 2).zero?
  sum = 1
end

p a
```

上記コードの出力結果は、`[2,4]`です。 **`inject`を使用したコードと、出力結果が異なってしまいます。**

上の二つのソースはあまり意味のないソースですし、実際はアキュムレータに別のオブジェクトを代入することはないと思います。
ただ、もしアキュムレータの変数に別のオブジェクトを代入した場合、`inject`メソッドの最後でメソッドの引数のオブジェクトを返却しているように見えたとしても、Rubocopは`inject`メソッドを`each_with_object`メソッドに置換できなくなるということです。

どういう処理になっているか詳しく知りたい場合は、`lib/rubocop/cop/style/each_with_object.rb`の`accumulator_param_assigned_to?`メソッド(プライベートメソッド)を見てください。

### それぞれのメソッドの使いどころは？
それでは、`inject`メソッドはどのような時に使えばよいのでしょうか。

```ruby:good_inject.rb
a = [1, 2, 3, 4].inject { |sum, n| sum + n }

p a
```
上記コードの出力結果は、`10`です。以下のような処理を行っています。

- 「ブロックの戻り値に要素の値を加算する」  
下記は詳細な処理内容です。
  1. メソッドの引数がないので、要素1(1)と要素2(2)をブロックに渡す
  1. 要素1+要素2(3)がブロックの戻り値となる
  1. ブロックの前回の戻り値(3)と要素3(3)をブロックに渡す
  1. ブロックの前回の戻り値+要素3(6)がブロックの戻り値となる
  1. ブロックの前回の戻り値(6)と要素4(4)をブロックに渡す
  1. ブロックの前回の戻り値+要素4(10)がブロックの戻り値となる
  1. **ブロックが最後に返した値** `10`がメソッドの戻り値となる

以上のことから、各メソッドの特徴をまとめると以下のようになります。

#### `inject`メソッドの特徴  
- メソッドに引数を渡すか渡さないか選択できる
- ブロックを使って要素の繰り返し計算を行える

####  `each_with_object`メソッドの特徴  
- メソッドに引数を必ず渡す
- ブロックを使ってメソッドの引数として渡されたオブジェクトを操作する  
(おそらく、要素についてよりも、メソッドの引数のオブジェクトに対する操作が処理のメインになっているはずです。)

上記の特徴を念頭に置いておくと、ソースを書いている際にメソッドの使いどころが思いつくかもしれません。  

## あとがき
難しかったです。まだ理解が及んでいない部分もあります。
`inject`メソッドはちょっとややこしい気もしますが、使うことでソースがかなりすっきり読みやすくなる場合もあります。
配列の操作をしていてコードがいまひとつだなと思った時は、下記のサイトで使えそうなメソッドを探すのがよいです。

- [オブジェクト指向スクリプト言語 Ruby リファレンスマニュアル (Ruby 2.6.0)](https://docs.ruby-lang.org/ja/latest/doc/index.html)  

## 配列の操作について参考になるサイト
- [Ruby: Enumerableを`reduce`で徹底理解する#1 基本編（翻訳）](https://techracho.bpsinc.jp/hachi8833/2018_07_17/57968)