injectのシンボルに指定できるメソッド色々

[`Enumerable#inject`](https://docs.ruby-lang.org/ja/latest/method/Enumerable/i/inject.html) の引数にシンボルを渡して[畳み込み（fold）](https://en.wikipedia.org/wiki/fold_(higher-order_function))を短く書くことがよくある。演算子である `+` や `*` などが多いが、一定の条件を満たすメソッドなら何でもいい。普段見ないような使い方ができないか探ってみた。

ちなみに**実用性ありそうなものは大抵もっといい方法がある**。何でもinjectを使おうとするのは~~楽しいけど~~良くない。

## ブロック省略の形
例えば `#map` では以下のようにブロックを省略できる。

```ruby
# 単純な処理ではこんな形になることがある
list.map { |item| item.mtd0 }
list.map { |item| object.mtd1(item) }
list.map { |item| hash_table[item] }

# 上のブロックは & を使って短縮できる
list.map(&:mtd0)
list.map(&object.method(:mtd1))
list.map(&hash_table)   # Ruby2.3から
```

Symbol, Method, Hash と脈絡のない省略規則に見えるが、これらは各クラスの `#to_proc` の定義に依存している。もちろん自分で定義してもいい。

`#inject` でも同様のことができる。**ブロックの引数が2つ**という違いに注意する。（※ `init` は省略に関係ない）

```ruby
# 単純な処理ではこんな形になることがある
list.inject(init) { |result,item| result.mtd1(item) }
list.inject(init) { |result,item| object.mtd2(result, item) }

# 上のブロックは & を使って短縮できる
list.inject(init, &:mtd1)
list.inject(init, &object.method(:mtd2))
```

上の省略は `#each_with_index` などでもできるが、 **`#inject` は引数に直接シンボル（または文字列）を取れる**ようにも定義されている。

```ruby
# injectは特別に以下の書き方もできる
list.inject(init, :mtd1)   # この書き方が一般的
list.inject(init, "mtd1")
```

この `mtd1` にどんなメソッドを使えるか見ていきたい。

---

ところで、総和を求めるときに `:+` を渡すのは、演算子 `+` がメソッドとして書けるため成り立っている。メソッドでない演算子 `&&` などは不可。

```ruby
list.inject(0, :+)  <=>  list.inject(0) { |result,item| result.+(item) }
```

## メソッドの条件
ループを展開すればわかる。「メソッド呼び出し `.mtd1(item)` に応答するオブジェクト」を返し続けられればいい。

```ruby
init.mtd1(list[0])
    .mtd1(list[1])
    .mtd1(list[2])
    ...
    .mtd1(list[-1])
```

しかし戻り値 `result` のクラスが毎回変わると調査がきつい。そこで `init` や `result` のクラスは変わらないという前提条件をおく。すると、

* `result.class` は `mtd1` というインスタンスメソッドを持つ
* `mtd1` は引数を1つとる（可変長引数も可）
* `mtd1` はブロック不要
* `mtd1` の戻り値は `result.class` である（継承やmix-inも考慮が必要）

という性質を満たす `result.class#mtd1` を探せばいいことになる。これくらいならマニュアルを漁って調べられそう。

<!--
```ruby
def arity1(m)
	m.instance_methods.select { |sym| [1, -1, -2].include? m.instance_method(sym).arity }.sort
end
```
-->

## 例
全部列挙すると無意味なものまで入ってしまうので、ある程度面白そうなものを挙げる。一部はinjectを使わない方法も言及する。

### Numeric, Integer
四則演算やビット演算など、ほとんどの二項演算はinjectで使える。演算子が無いものだと最大公約数あたりが便利。

```ruby
# 最大公約数と最小公倍数
[105, 140, 168].inject(0, :gcd)   #=>  7
[ 12,  15,  20].inject(1, :lcm)   #=> 60
```

`+` の場合は [`sum`](https://docs.ruby-lang.org/ja/2.4.0/method/Enumerable/i/sum.html) を使ったほうがいい（Ruby2.4以上）。

```ruby
([0.1] * 10000).inject(0, :+)   #=> 1000.0000000001588
([0.1] * 10000).sum(0)          #=> 1000.0
```

### String
`+`, `<<` など結合するものはわかりやすい。でも `join` や `concat` で済む。

```ruby
["ab", "cd", "ef"].inject("", :<<)   #=> "abcdef"
"".concat("ab", "cd", "ef")          #=> "abcdef"
```

`concat` とは逆に、可変長引数を利用すると意味が変わるメソッドがあった。

```ruby
# 文字の除去
["2-6", "^4-8"].inject("123456789", :delete)   #=> "78"
"123456789".delete("2-6", "^4-8")              #=> "1456789"

# squeezeも異なる結果になる
```

<!--
文字列を返すメソッドは多いのだが、injectできて且つ面白そうなのがあまり見つからない…

```ruby
["%s %f", ["%s %d", 3], [1, 2]].inject("%s", :%)   #=> "1 2 3.000000"

[/\w+/, 2..4, 1].inject("abc123", :[])   #=> "1"

(2..5).inject("a", :center)   #=> "a    "

%w(ab cd ef).inject("123", :crypt)   #=> "efLlboUXIUjzw"
```
-->

### Enumerable, Array, Hash
String 同様 `+`, `<<` など結合するものはわかりやすい。 `flatten(1)` で済んだり、 `concat` や `push` のように可変長引数なものがあるのも同じ。 `Hash#merge` は~~代替手段が無さそう~~Ruby2.6から複数のマージに対応した。

```ruby
# ハッシュの結合
[{a: 0, b: 1}, {a: 2, c: 3}, {b: 4, c: 5}].inject({}, :merge)
#=> {:a=>2, :b=>4, :c=>5}

# Ruby2.6以降
{}.merge({a: 0, b: 1}, {a: 2, c: 3}, {b: 4, c: 5})
#=> {:a=>2, :b=>4, :c=>5}
```

Array, Enumerator, Object という風に色々なオブジェクトを返すメソッドがあるので、injectも色々なパターンができる。

```ruby
# and検索
[/in/, /[^a-z]/, /^.{,8}$/].inject(777.methods, :grep)
#=> [:to_int, :integer?, :finite?, :kind_of?, :tainted?]

# 多次元配列化
md_ary = [3, 2].inject(0...12, :each_slice).to_a
#=> [[[0, 1, 2], [3, 4, 5]], [[6, 7, 8], [9, 10, 11]]]

# 多次元配列の要素を参照
[0, 1, 2].inject(md_ary, :[])      #=> 5
[1, 2, 3].inject(md_ary, :[])      #=> NoMethodError (undefined method `[]' for nil:NilClass)
[1, 2, 3].inject(md_ary, :fetch)   #=> IndexError (index 2 outside of array bounds: -2...2)
```

`[]` と `fetch` は Array と Hash の両方にあるので、JSONのように互いに入れ子になっていても大丈夫。Ruby2.3以降は [`dig`](https://docs.ruby-lang.org/ja/2.3.0/method/Array/i/dig.html) で一気に参照できる。

```ruby
md_ary.dig(0, 1, 2)   #=> 5
md_ary.dig(1, 2, 3)   #=> nil
```

### TrueClass と FalseClass
論理演算子 `&`, `|`, `^` が共通して存在し、戻り値もどちらかになるのでinjectできる。戻り値のクラスが異なってもいい単純な例。

```ruby
list = [true, false, nil, 0, ""]
list.inject(true , :&)   #=> false
list.inject(false, :|)   #=> true
list.inject(false, :^)   #=> true
```

AND, ORは直接的に求められるメソッドがある。XORは「真の個数の偶奇」を考える。

```ruby
list.all?                   #=> false
list.any?                   #=> true
list.count(&:itself).odd?   #=> true
```

### BasicObject
全てのクラスの親である BasicObject のインスタンスメソッドなら、ほぼ全てのオブジェクトに対して使える。

```ruby
# メソッドを連続して作用させる
%i(class singleton_class superclass).inject(777, :__send__)   #=> #<Class:Numeric>

# 任意のコードの結果を次のコードの `self` にする
(<<EOS.lines).inject(777, :instance_eval)
p self.class
p singleton_class
p superclass
EOS
```

### その他
injectは引数をひとつだけとることも可能なので、**injectのinject**もできる。とはいえ真っ当な例を見つけられていない。

```ruby
%i(divmod coerce divmod).inject([1000, 7], :inject)   #=> [0, 6]
```

せっかくブロック省略で紹介したので `mtd2` の例もひとつ。

```ruby
# n次元ベクトルの長さ
[8, 9, 12].inject(0, &Math.method(:hypot))   #=> 17.0

[8, 9, 12].sum(&:abs2) ** 0.5                #=> 17.0
Vector[8, 9, 12].norm                        #=> 17.0
```