From: Robert Dober Date: 2008-11-20T08:36:20+09:00 Subject: Re: Unit Conversion (#183) ------=_Part_29100_1594400.1227137618515 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Content-Disposition: inline Well time is difficult to get but I just had to supply a Ruby Quiz submission *again*. Was so much fun. Tried to implemnt Tom's syntax driven by a data file (tiny :(, see above) # file: units.txt # # Units data file for Ruby Quiz #183 # # ----------------------------------- # 1 in =3D 0.0254 m 1 l =3D 0.001 m3 use SI prefixes for m g l m3 _____________________________________________ # file: units.rb module Conversion # # A primitive parser to create the conversion data structure, syntax checks are # omitted to shorten the solution ;). # class LineParser SIUnits =3D { "Y" =3D> 10**24, "Z" =3D> 10**21, "E" =3D> 10**18, "P" = =3D> 10**15, "T" =3D> 10**12, "G" =3D> 10**9, "M" =3D> 1_000_000, "k" =3D> 1000, "h" =3D> 100, "D" = =3D> 10, "d" =3D> 0.1, "c" =3D> 0.01, "m" =3D> 0.001, "=B5" =3D> 0.000_001, "n= " =3D> 10**-9, "p" =3D> 10**-12, "f" =3D> 10**-15, "a" =3D> 10**-18, "z"=3D> 10**-21, "y" =3D> 10**-24= } def parse_line line return if /^\s*#/ =3D=3D=3D line || line.strip.empty? line =3D line.strip.sub /#.*/, "" case line when /^use SI prefixes for / =09add_si_units line.split[4..-1] else =09add_conversion *line.split end end def traverse &blk @c.each do | unit, conversion_hash | =09_traverse unit, conversion_hash, [ unit ], &blk end end private def add_conversion lhs_value, lhs_unit, equal_dummy, rhs_value, rhs_uni= t @c[ lhs_unit ][ rhs_unit ] =3D Float( rhs_value ) / Float( lhs_value = ) @c[ rhs_unit ][ lhs_unit ] =3D Float( lhs_value ) / Float( rhs_value = ) end def add_si_units units units.each do |unit| =09add_si_unit_for unit end end def add_si_unit_for unit SIUnits.each do | prefix, conversion | =09@c[ prefix + unit ][ unit ] =3D conversion =09@c[ unit ][ prefix + unit ] =3D 1 / conversion end end def initialize @c =3D Hash::new{ | h, k | h[ k ] =3D { } } end def _traverse src_unit, unit_conversions, traversed_units, f=3D1.0, &bl= k unit_conversions.each do | new_unit, conversion | =09next if traversed_units.include? new_unit =09blk.call src_unit, new_unit, f * conversion =09_traverse src_unit, @c[ new_unit ], traversed_units + [ new_unit ], f * conversion, &blk end end end # # Will be subclassed for each unit. # Each such sublcass will than get all conversion methods defined dynami= cally # by means of exploring "units.txt" # class ProxyClass # syntactic sugar def to; self end alias_method :as, :to private def initialize value @value =3D value end end ProxyClasses =3D {} # maps units to their proxy classes def method_missing unit_name pc =3D ProxyClasses[ unit_name.to_s ] || super( unit_name ) pc::new self end conversions =3D LineParser::new File::open "units.txt" do | f | f.each do | line | conversions.parse_line line end end conversions.traverse do | src_unit, tgt_unit, conversion | ( ProxyClasses[ src_unit ] ||=3D Class::new ProxyClass ).module_eval do define_method tgt_unit do (@value * conversion).to_s + tgt_unit end end end end class Integer include Conversion end class Float include Conversion end _________________________________________________________________ # file: test-solution.rb require 'units' require 'test/unit' class Test183_01 < Test::Unit::TestCase def test_001_simple assert_equal "25.4mm", 1.0.in.to.mm assert_equal "1.0mm", 0.001.m.to.mm assert_equal "0.0393700787401575in", 1.mm.in assert_equal "39370.0787401575in", 1.km.as.in assert_equal "0.001m3", 1.0.l.as.m3 end end Thx for the quiz. --=20 Ne baisse jamais la t=EAte, tu ne verrais plus les =E9toiles. Robert Dober ;) ------=_Part_29100_1594400.1227137618515 Content-Type: application/x-gzip; name=183-robert-dober.tgz Content-Transfer-Encoding: base64 X-Attachment-Id: f_fnqm13g60 Content-Disposition: attachment; filename=183-robert-dober.tgz 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 WgAoAAA= ------=_Part_29100_1594400.1227137618515--