Ruby

Ruby|配列をどう変換するか

retryableというgemがあります。
キーワード引数matchingを使って補足する例外を指定することができます。

def retryable(tries:, matching: StandardError)
  begin
    # なんらかのテクニックが必要
  rescue *matching => e # このままでは動かない
    puts e.message
  end
end

ここで話題にしたいのはmatchingには例外クラスを配列で指定するか、
例外クラスを1つだけのどちらでも使えるようにするテクニックです。

rescueで例外を補足する必要がありますので1次元配列(入れ子でない)にする必要があります。
私がおもいついたのは3つです。retryableではflattenを利用しているようです。

https://github.com/nfedyashev/retryable/blob/master/lib/retryable.rb#L63

matching = matching.is_a?(Array) ? matching : [matching]
matching = [matching].flatten
matching = Array[*matching]

これらのベンチマークを取ってみました。

require 'benchmark_driver'

Benchmark.driver do |x|
  x.prelude %{ array = [*1..5000] }
  x.report "flatten", %{ [array].flatten }
  x.report "using Array.[]", %{ Array[*array] }
  x.report "Object#is_a?", %{ array.is_a?(Array) ? array : [array] }
  x.compare!
end

なんと結果はflattenが一番遅いことがわかりました:sweat_smile:

Warming up --------------------------------------
             flatten     1.869k i/s
      using Array.[]    56.587k i/s
        Object#is_a?    16.534M i/s
Calculating -------------------------------------
             flatten     2.029k i/s -      5.607k times in 2.763675s (492.90μs/i)
      using Array.[]    54.798k i/s -    169.760k times in 3.097910s (18.25μs/i)
        Object#is_a?    30.039M i/s -     49.602M times in 1.651260s (33.29ns/i)

Comparison:
        Object#is_a?:  30038651.1 i/s 
      using Array.[]:     54798.2 i/s - 548.17x  slower
             flatten:      2028.8 i/s - 14805.97x  slower

ということでPRを本家に出しました:tada:
マージされるといいなぁ:smile:

https://github.com/nfedyashev/retryable/pull/22