[QUIZ] Unit Conversion (#183)

It is cheating.

However, I never told him not to do it. :wink:

Seriously, this is Ruby Quiz. I'm not going to call the cops on you because you did something other than what I expected.

···

On Nov 15, 2008, at 8:07 PM, Ken Bloom wrote:

On Fri, 14 Nov 2008 14:20:42 -0500, Matthew Moss wrote:

Your task is to write a units converter script. The input to the
script must be three arguments: the quantity, the source units, and
the destination units. The first example above would be run like this:

    $ ruby convert.rb 50 miles kilometers

Or, using abbreviations:

    $ ruby convert.rb 50 mi km

Support as many units and categories of units (i.e. volume, length,
weight, etc.) as you can, along with appropriate abbreviations for
each unit.

This will be interesting. I'm not going to endeavor into myself b/c I
help maintain Stick (http://stick.rubyforge.org) which already does
this (and there's another lib out there that does it too). But it will
be interesting to see how others approach it.

Note to everyone else: using another lib, such as stick, is considered
cheating for this quiz. :smiley:

I disagree, and think that if he actually has a useful lib, he should
show it off.

For what it's worth, I always invited people to submit whatever they wanted when I ran the quiz. The more the merrier, I figure. Beside, I was just going to summarize whatever the heck I wanted to anyway. :wink:

James Edward Gray II

···

On Nov 15, 2008, at 8:07 PM, Ken Bloom wrote:

On Fri, 14 Nov 2008 14:20:42 -0500, Matthew Moss wrote:

Your task is to write a units converter script. The input to the
script must be three arguments: the quantity, the source units, and
the destination units. The first example above would be run like this:

    $ ruby convert.rb 50 miles kilometers

Or, using abbreviations:

    $ ruby convert.rb 50 mi km

Support as many units and categories of units (i.e. volume, length,
weight, etc.) as you can, along with appropriate abbreviations for
each unit.

This will be interesting. I'm not going to endeavor into myself b/c I
help maintain Stick (http://stick.rubyforge.org) which already does
this (and there's another lib out there that does it too). But it will
be interesting to see how others approach it.

Note to everyone else: using another lib, such as stick, is considered
cheating for this quiz. :smiley:

I disagree, and think that if he actually has a useful lib, he should
show it off. When I wrote Quiz #95 (Code to S-Exp), it was useful to me
to see the rubynode and ParseTree solutions, even if some would have
considered that cheating. I wound up using the rubynode version for the
Ruby 1.8 version of the library I was writing.

(Or maybe if you say it's cheating, that means he's allowed to talk about
it during the spoiler period.)

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

183-robert-dober.tgz (1.51 KB)

···

#
# Units data file for Ruby Quiz #183
#
# -----------------------------------
#
1 in = 0.0254 m
1 l = 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 = { "Y" => 10**24, "Z" => 10**21, "E" => 10**18, "P" =>
10**15, "T" => 10**12,
      "G" => 10**9, "M" => 1_000_000, "k" => 1000, "h" => 100, "D" => 10,
      "d" => 0.1, "c" => 0.01, "m" => 0.001, "µ" => 0.000_001, "n" =>
10**-9, "p" => 10**-12,
      "f" => 10**-15, "a" => 10**-18, "z"=> 10**-21, "y" => 10**-24 }

    def parse_line line
      return if /^\s*#/ === line || line.strip.empty?
      line = line.strip.sub /#.*/, ""
      case line
      when /^use SI prefixes for /
  add_si_units line.split[4..-1]
      else
  add_conversion *line.split
      end
    end

    def traverse &blk
      @c.each do | unit, conversion_hash |
  _traverse unit, conversion_hash, [ unit ], &blk
      end
    end
    private
    def add_conversion lhs_value, lhs_unit, equal_dummy, rhs_value, rhs_unit
      @c[ lhs_unit ][ rhs_unit ] = Float( rhs_value ) / Float( lhs_value )
      @c[ rhs_unit ][ lhs_unit ] = Float( lhs_value ) / Float( rhs_value )
    end

    def add_si_units units
      units.each do |unit|
  add_si_unit_for unit
      end
    end

    def add_si_unit_for unit
      SIUnits.each do | prefix, conversion |
  @c[ prefix + unit ][ unit ] = conversion
  @c[ unit ][ prefix + unit ] = 1 / conversion
      end
    end

    def initialize
      @c = Hash::new{ | h, k | h[ k ] = { } }
    end

    def _traverse src_unit, unit_conversions, traversed_units, f=1.0, &blk
      unit_conversions.each do | new_unit, conversion |
  next if traversed_units.include? new_unit
  blk.call src_unit, new_unit, f * conversion
  _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 dynamically
  # by means of exploring "units.txt"
  #
  class ProxyClass
    # syntactic sugar
    def to; self end
    alias_method :as, :to

    private
    def initialize value
      @value = value
    end
  end

  ProxyClasses = {} # maps units to their proxy classes
  def method_missing unit_name
    pc = ProxyClasses[ unit_name.to_s ] || super( unit_name )
    pc::new self
  end

  conversions = 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 ] ||= 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.
--
Ne baisse jamais la tête, tu ne verrais plus les étoiles.

Robert Dober :wink:

Don't forget mass can be converted to energy :slight_smile:

And patrol to dollars.
Not to mention distance to time. Well it seems at least, whenever I
ask how far is it away I get answers like 1 hour. :wink:

Excellent quiz! Waiting for some good answers!

1+
Robert

For what it's worth, I always invited people to submit whatever they wanted when I ran the quiz. The more the merrier, I figure. Beside, I was just going to summarize whatever the heck I wanted to anyway. :wink:

Agreed. I said it was cheating, but I didn't say "don't cheat." :smiley:

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)

If this were a a contest, looks like you get the blue ribbon :slight_smile:

# file: units.txt
#
# Units data file for Ruby Quiz #183
#
# -----------------------------------

Impressed you got that much functionality from just this little bit of
code.

What up with "µ" => 0.000_001 :wink:

T.

···

On Nov 19, 6:36 pm, "Robert Dober" <robert.do...@gmail.com> wrote:

It would be much more fun to include some non static conversion
variables. Like:
ruby convert.rb 30 euros dollars
# Just making things more interesting

Agreed. I said it was cheating, but I didn't say "don't cheat." :smiley:

Sorry for nitpicking, but it is *not* cheating. It might not be what
you want and it is important to know what you consider a valid
submission or not. But cheating induces fraud, deceit or dishonesty. I
guess any solution having a proud
require 'mylib'
at the top does not have any of these negative properties.

Not that I want to make your job harder than it is already but
solutions that are submitted here might be interesting for the humble
reader even if they do not hit the spirit of the quiz on its
sweet-spot, and you might not review them. Would you tend to agree on
this?

Cheers
Robert

···

--
We are all in the gutter, but some of us are looking at the stars.

Oscar Wilde

I do not really merit this praise, though I appreciate it;). Actually
the metaprogramming part is about ok, but the
datastructure is quite primitive, well if only I had more time (or
were smarter LOL).
Cheers
R.

···

On Thu, Nov 20, 2008 at 6:22 PM, Trans <transfire@gmail.com> wrote:

On Nov 19, 6:36 pm, "Robert Dober" <robert.do...@gmail.com> wrote:

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)

If this were a a contest, looks like you get the blue ribbon :slight_smile:

# file: units.txt
#
# Units data file for Ruby Quiz #183
#
# -----------------------------------

Impressed you got that much functionality from just this little bit of
code.

What up with "µ" => 0.000_001 :wink:

T.

Go for it! :smiley:

···

On Nov 16, 2008, at 11:31 AM, Rubén Medellín wrote:

It would be much more fun to include some non static conversion
variables. Like:
ruby convert.rb 30 euros dollars
# Just making things more interesting

Agreed. I said it was cheating, but I didn't say "don't cheat." :smiley:

Sorry for nitpicking, but it is *not* cheating.

Provide a simple, better word for "going against intended guidelines" and I'll consider it.

In reality, I think most people understand that it is being used tongue-in-cheek. And, as I stated, "cheating" is acceptable.

It might not be what
you want and it is important to know what you consider a valid
submission or not.

Basically, anything within reason that solves the quiz problem is valid, "cheating" or not.

Not that I want to make your job harder than it is already but

solutions that are submitted here might be interesting for the humble
reader even if they do not hit the spirit of the quiz on its
sweet-spot, and you might not review them. Would you tend to agree on
this?

How about just writing a solution that solves the quiz -- "cheating" if you like, not "cheating" if you don't like -- and leave the spirits to go haunt some Perl coder. I never have time to fully review all the solutions, "cheating" or not. But I generally give everything a once-over. Just because you do something that I've called cheating will not automatically send your submission to the trash bin. On the contrary, I'll probably point it out as "the right way" to do things.

If your main issue is that you think "cheat" implies dishonesty, then understand I mean no such thing. gsub(/cheat/, "reuse") and you'll be happier.

···

On Nov 17, 2008, at 4:27 AM, Robert Dober wrote:

If your main issue is that you think "cheat" implies dishonesty, then
understand I mean no such thing. gsub(/cheat/, "reuse") and you'll be
happier.

No problem at all, I have however taken some time to explain why I was
somehow shocked OL.
I apologize if this is kind of wasting bandwidth on the Quiz Thread.

I just wanted to submit my first Ruby-quiz solution ever - but I was waiting for the other solutions, being unsure when does the 48-hour deadline end - but by now I am pretty sure it's over. So what's going on?

Cheers,
Peter

···

___
http://www.rubyrailways.com
http://scrubyt.org

On 2008.11.17., at 21:09, Robert Dober wrote:

If your main issue is that you think "cheat" implies dishonesty, then
understand I mean no such thing. gsub(/cheat/, "reuse") and you'll be
happier.

No problem at all, I have however taken some time to explain why I was
somehow shocked OL.
I apologize if this is kind of wasting bandwidth on the Quiz Thread.

Yes, the quiz is (usually) posted Friday, so submissions typically start rolling in Sunday. And Martin did provide his alcohol submission yesterday. So feel free to post.

···

On Nov 18, 2008, at 11:50 AM, Peter Szinek wrote:

I just wanted to submit my first Ruby-quiz solution ever - but I was waiting for the other solutions, being unsure when does the 48-hour deadline end - but by now I am pretty sure it's over. So what's going on?

Yes, the quiz is (usually) posted Friday, so submissions typically start rolling in Sunday. And Martin did provide his alcohol submission yesterday. So feel free to post.

OK. <drumroll> my first submission to Ruby-quiz:

(After all those discussion on cheating, I am not sure if this solution is not considered cheating of some sort :wink:

···

===============================================================
require 'rubygems'
require 'cgi'
require 'scrubyt'

begin
google_converter = Scrubyt::Extractor.define do
   fetch "http://www.google.com/search?q=#{CGI::escape(ARGV[0])}+#{CGI::escape(ARGV[1])}+to+#{CGI::escape(ARGV[2])\}&quot;

   google_result "//td[@dir='ltr']" do
     final_result(/= (.+) /)
   end
end
   puts google_converter.to_hash[0][:final_result]
rescue
   puts "Sorry, even *google* can't translate that!"
end

ex:
ruby converter.rb 10 "meter per second" "mile per hour"
22.3693629

ruby converter.rb 10 USD EUR
7.91201836

ruby converter.rb 7 "ruby gems" "python eggs"
Sorry, even *google* can't translate that!
etc.

disadvantage: you need to be online
advantage: it's quite rich, robust and up-to date (e.g. currency conversions)

I am wondering if this solution is (at least somewhat) OK - I have a similar one, though that doesn't require you to be on-line. However, I am not going to implement it if the above solution is BS :slight_smile:

Cheers,
Peter
___
http://www.rubyrailways.com
http://scrubyt.org

> Yes, the quiz is (usually) posted Friday, so submissions typically
> start rolling in Sunday. And Martin did provide his alcohol
> submission yesterday. So feel free to post.

OK. <drumroll> my first submission to Ruby-quiz:

(After all those discussion on cheating, I am not sure if this
solution is not considered cheating of some sort :wink:

===============================================================
require 'rubygems'
require 'cgi'
require 'scrubyt'

begin
google_converter = Scrubyt::Extractor.define do
fetch "Google
+#{CGI::escape(ARGV[1])}+to+#{CGI::escape(ARGV[2])}"

google_result "//td[@dir='ltr']" do
final_result(/= (.+) /)
end
end
puts google_converter.to_hash[0][:final_result]
rescue
puts "Sorry, even *google* can't translate that!"
end

ex:
ruby converter.rb 10 "meter per second" "mile per hour"
22.3693629

ruby converter.rb 10 USD EUR
7.91201836

ruby converter.rb 7 "ruby gems" "python eggs"
Sorry, even *google* can't translate that!
etc.

disadvantage: you need to be online
advantage: it's quite rich, robust and up-to date (e.g. currency
conversions)

I am wondering if this solution is (at least somewhat) OK - I have a
similar one, though that doesn't require you to be on-line. However, I
am not going to implement it if the above solution is BS :slight_smile:

I think this is an interesting solution b/c it is prescient of a
future we are likely to see. When average broadband connections
approach 1gbs or so this kind of thing will be the norm. We are seeing
this evolution now, with software not being installed by physical
media any longer, but via online package management. And now with
things like RubyGems it is becoming even more fine grained.

Ultimately "usr/lib" will be on the wire.

So what is your similar solution? Taking a guess... is it this with a
cache?

T.

···

On Nov 18, 2:28 pm, Peter Szinek <pe...@rubyrailways.com> wrote:

Cheers,
Peter
___http://www.rubyrailways.comhttp://scrubyt.org

...
# Ultimately "usr/lib" will be on the wire.

totally.

i would wish something like,

require "http://some.site/some.rb"

or
gems that's requireable (no need for require rubygems)

require "http://some.site/some.gem"

or
or just plain require a zipful of rbs/gems/othergz...

require "http://some.site/some.gz"

···

From: Trans [mailto:transfire@gmail.com]