長生村本郷Engineers'Blog

千葉県長生村本郷育ちのエンジニアが書いているブログ

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

f:id:kenzo0107:20191123143247p:plain

概要

お題を通して 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 メソッド

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

gist.github.com

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

[] + 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 メソッド

gist.github.com

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

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

benchmark とってみる

require "benchmark"

r = 123_456

Benchmark.bmbm do |x|
    x.report("div_inject") do
        10000.times do
            div_inject(r)
        end
    end

    x.report("div_each") do
        10000.times do
            div_each(r)
        end
    end
end

each の方が速かったです。

                 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

gist.github.com

each

gist.github.com

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

まとめ

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

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

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