Rubyでゼロ除算になるかもしれないときの書き方
やりたいこと
分母に0が来る可能性がある除算をRubyで書くときに、すっきり書きたい。
考えるケース
店(Shop
)がそれぞれ複数の在庫(Stock
)を持っているシンプルなケースを考える。イメージとしては下のような感じ。
class Shop has_many :stocks end class Stock attr_accessor :price end
このケースで店舗にある在庫の平均価格を求めるメソッドShop#average_price
を定義する。ただし、在庫が1つもないときは0を返す。*1
1. 素直に書いてみる
class Shop def average_price stocks.size > 0 ? stocks.map(&:price).reduce(0, :+) / stocks.size : 0 end end
世の中の多くのコードはこのパターンで書かれている気がする。よく使われている分、わかりやすいと言えばわかりやすいが、stocks.size
が2箇所に現れているのが気になる。
2. 例外を使うパターン その1
class Shop def average_price stocks.map(&:price).reduce(0, :+) / stocks.size rescue ZeroDivisionError 0 end end
個人的に好きな書き方。欲しい物をシンプルに書いて、ゼロ除算の例外を補足する。
メソッド中などのようにreturn
する場合は良いが、返り値をそのまま変数に代入したい場合にはあまり実用的でないのがデメリット。
foo = begin 100 / 0 rescue ZeroDivisionError 0 end
3. 例外を使うパターン その2
class Shop def average_price stocks.map(&:price).reduce(0, :+) / stocks.size rescue 0 end end
「例外を使うパターン その1」で扱いにくかった変数への代入に使えるパターン。
foo = 100 / 0 rescue 0
デメリットとしてはZeroDivisionError
以外の例外も握りつぶしてしまうこと。
まとめ
どれも一長一短という感じです。
その場でしか使わないような数値に対しては最も一般的な1のパターンも使っていきますが、理想としては丁寧にメソッドを切り出した中でZeroDivisionError
を補足するのが良いと思っています。
参考:きわめて強いゼロ(very strongly zero)*2
『コンピュータの数学』のp.26にきわめて強いゼロ(very strongly zero)というものが載っている。
きわめて強いゼロに対する演算は
- 定義されていない値の乗算を行なっても
- 0による除算を行なっても
結果が0になる。