Ruby inject (Enumerable) メソッドを学ぶ

概要

お題を通して Ruby で inject メソッドを学びました。その備忘録です。

お題

123,456 円を紙幣・硬貨が一番少なくなる様になる組み合わせを求めてください。
硬貨 … 1, 5, 10, 50, 100, 500 円玉
紙幣 … 1000, 2000, 5000, 10000 円札

答え

  • 10,000 円札 × 12
  • 5,000 円札 × 0
  • 2,000 円札 × 1
  • 1,000 円札 × 1
  • 500 円玉 × 0
  • 100 円玉 × 4
  • 50 円玉 × 1
  • 10 円玉 × 0
  • 5 円玉 × 1
  • 1 円玉 × 1

inject メソッド

配列の組み合わせで答えを出すとシンプルに書けます。

大きい値から順に divmod の結果 [商, 余り] を不定数個の配列 (a) の後ろに足して、
r に配列の一番後ろの要素 余り が入るので、それ次の iterator の計算に利用する方法です。

1
2
3
4
5
6
7
8
9
10
[] + 123456.divmode(10000) # [12, 3456]
[12, 3456] + 3456.divmod(5000) # [12, 0, 3456]
[12, 0, 3456] + 3456.divmod(2000) # [12, 0, 1, 1456]
[12, 0, 1, 1456] + 1456.divmod(1000) # [12, 0, 1, 1, 456]
[12, 0, 1, 1, 456] + 456.divmod(500) # [12, 0, 1, 1, 0, 456]
[12, 0, 1, 1, 0, 456] + 456.divmod(100) # [12, 0, 1, 1, 0, 4, 56]
[12, 0, 1, 1, 0, 456] + 56.divmod(50) # [12, 0, 1, 1, 0, 4, 1, 6]
[12, 0, 1, 1, 0, 4, 1, 6] + 6.divmod(10) # [12, 0, 1, 1, 0, 4, 1, 1, 0, 6]
[12, 0, 1, 1, 0, 4, 1, 1, 0, 6] + 6.divmod(5) # [12, 0, 1, 1, 0, 4, 1, 1, 0, 1]
[12, 0, 1, 1, 0, 4, 1, 1, 0, 1] + 1.divmod(1) # [12, 0, 1, 1, 0, 4, 1, 1, 0, 1, 1, 0]

最後の 0 が余計ですが、非常にシンプルな形で答えを求られました。

ただ、
この inject の計算式が初手でスッと出すのは慣れが必要な印象。

なので、
一旦 each で計算の手順を確認してから inject でコードがシンプルになりそうならやってみる、
っていうのが慣れるのに良さそうでした。

each メソッド

each でやると loop の外側で b = [] と最終的に返す配列を定義する必要があります。

b << r if p.size - 1 == index は、不要ですが、回答の配列を inject メソッドの場合と合わせました。

benchmark とってみる

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
require &quot;benchmark&quot;

r = 123_456

Benchmark.bmbm do |x|
x.report(&quot;div_inject&quot;) do
10000.times do
div_inject(r)
end
end

x.report(&quot;div_each&quot;) do
10000.times do
div_each(r)
end
end
end

each の方が速かったです。

1
2
3
                 user     system      total        real
div_inject 0.037365 0.000524 0.037889 ( 0.040733)
div_each 0.024333 0.000257 0.024590 ( 0.025320)

個人的には inject の方が速くなってくれると本記事が締まったんですが
今回の計算ロジックでは、each の方がパフォーマンスがよかったです。

hash を扱ってみる

inject

each

benchmark とると、 each, hash どちらもほぼ同等でした。

まとめ

inject メソッドを例題を通して学びました。

例題では特にシンプルに書けましたが、パフォーマンスが格段に上がるという話ではないので
使い所は見定める必要があるかなと思いました。

以上
ご参考になれば幸いです。

Author

Kenzo Tanaka

Posted on

2019-11-26

Updated on

2020-05-07

Licensed under

コメント