class Length YARD = { :barleycorn => ((1.0 / 3) / 12) / 3, :in => (1.0 / 3) / 12, :ft => 1.0 / 3, :yard => 1.0, :pole => 5.5, :chain => 5.5 * 4, :furlong => 5.5 * 4 * 10, :ml => 5.5 * 4 * 10 * 8, :league => 5.5 * 4 * 10 * 8 * 3, } METRIC = { :Ym => 10 ** 24, :Zm => 10 ** 21, :Em => 10 ** 18, :Pm => 10 ** 15, :Tm => 10 ** 12, :Gm => 10 ** 9, :Mm => 10 ** 6, :km => 10 ** 3, :hm => 10 ** 2, :dam => 10, :m => 1, :dm => 10 ** -1, :cm => 10 ** -2, :mm => 10 ** -3, :um => 10 ** -6, :nm => 10 ** -9, :pm => 10 ** -12, :fm => 10 ** -15, :am => 10 ** -18, :zm => 10 ** -21, :ym => 10 ** -24, } UNITS = YARD.keys + METRIC.keys RATIO = 0.0254 YARD.each do |unit, ratio| define_method(unit) { self.new(self.to_yard.number * ratio, unit) } end METRIC.each do |unit, ratio| define_method(unit) { self.new(self.to_metric.number * ratio, unit) } end def initialize(num, unit) raise "Unsupported units!" unless UNITS.include?(unit) @number = num @unit = unit end def to_metric if self.is_metric? self else new(self.inch.number * RATIO, :m) end end def to_yard if self.is_yard? self else new(self.m.number / RATIO, :inch) end end def is_metric? METRIC.keys.include?(@unit) end def is_yard? YARD.keys.include?(@unit) end private def number @number end def unit @unit end end class Numeric Length::YARD.merge(Length::METRIC).each do |unit, ratio| define_method(unit) { Length.new(self * ratio, unit) } end end
なんでもいいから、定期的にコードを書く習慣をつけたくて、書いてみた。きっと既にあるんだろうけど、筋トレみたいなものだし、知らないふりをする。
いろいろバグありそう。長さの単位を表すメソッド (cm とか yard) がとても短いのでいろいろよくない。実質的な計算は定数の中でほぼ済んでいるという、なんとも奇妙なコード。
Ruby で書いたコードは文字列処理とかメタプログミングばかりで、数値は四則計算くらいしか触ったことがなかった。べき乗の演算子って ^ だっけ、などとおもったらビットシフトだったでござる。
Wikipedia で単位系を調べるのに一番時間を費したという。
TODO:
- <=> メソッドを実装する。 Comparable を Mix-in するのに必要。
- 四則演算への対応。