[#319108] Iterator objects and lazy evaluation — Yuh-Ruey Chen <maian330@...>

Two questions:

14 messages 2008/11/01

[#319117] Poor performance of Ruby 1.8.7 when installed with MacPorts — abc <arcadiorubiogarcia@...>

Hi,

15 messages 2008/11/01

[#319176] Ruby for Philosophers — Sebastian Torena <citizenkant@...>

Hi there,

14 messages 2008/11/02

[#319196] ruby1.9: lazy versions of Enumerator#select and friends? — Brian Candler <b.candler@...>

I've been having a play with Enumerators in ruby 1.9, in particular

12 messages 2008/11/02

[#319239] Rake task for building latex? — Stefano Crocco <stefano.crocco@...>

Does anyone know whether there's a rake task to build latex files, including

10 messages 2008/11/03

[#319319] Ruby's take on S.O.L.I.D. — Mike Lopke <reglopke@...>

I'm curious about everyone's take on Bob Martin's S.O.L.I.D. design

16 messages 2008/11/03

[#319401] Combination of numbers in an array that add up to x — Hae Lee <hae.lee.subscription@...>

Objective: Find list of values in an array that adds up to a specific

17 messages 2008/11/04

[#319404] Showing a spinner ? — Aldric Giacomoni <aldric@...>

How do I show a spinner on the command line interface when a ruby script

13 messages 2008/11/04

[#319440] What would you like to know about JRuby? — Charles Oliver Nutter <charles.nutter@...>

Tom Enebo and I are putting together our JRuby presentation for

22 messages 2008/11/04

[#319532] What's the Best Way to Mimic an HTTP Request? — Daniel Miessler <daniel@...>

I'm trying to write a tool that will take a domain as an argument and

10 messages 2008/11/05

[#319546] Ruby has a Face that it wears on its feet — "Jayson Williams" <williams.jayson@...>

In my opinion, Ruby's official face should be Shoes. Shoes gives Ruby

17 messages 2008/11/05
[#319553] Re: Ruby has a Face that it wears on its feet — "Martin DeMello" <martindemello@...> 2008/11/05

On Wed, Nov 5, 2008 at 11:17 AM, Jayson Williams

[#319576] how to quickly find a string towards the end of a large io object — bwv549 <jtprince@...>

How do I scan starting at the end of a big io object to find a string

12 messages 2008/11/06

[#319702] Sudoku Generator (#182) — Matthew Moss <matt@...>

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

14 messages 2008/11/07

[#319769] implementing mvc - using observer pattern - beginner to OOP — Adam Akhtar <adamtemporary@...>

Hi I started making a simple command line todo list application as a way

10 messages 2008/11/08

[#319770] what's easiest way to compare a Float & BigDecimal (i.e. like a equals mechanism) — "Greg Hauptmann" <greg.hauptmann.ruby@...>

Hi,

8 messages 2008/11/08

[#319835] Moving large amount of files, 1.750.000+ — Sebastian Newstream <abeansits@...>

Hello fellow Rubyists!

15 messages 2008/11/09
[#319837] Re: Moving large amount of files, 1.750.000+ — Robert Klemme <shortcutter@...> 2008/11/09

On 09.11.2008 18:04, Sebastian Newstream wrote:

[#319861] Notepad++ (no debug output, using XP) — Ed Hardy <asm.sol@...>

Notepad++ seems to be a great editor for Ruby, in XP Windows. However,

13 messages 2008/11/10

[#319902] Problem with object methods? — Carter Davis <theshakrah@...>

I recently made an object for a game I'm making. It uses the constructor

16 messages 2008/11/10
[#319908] Re: Problem with object methods? — Hugh Sasse <hgs@...> 2008/11/10

[#319911] Re: Problem with object methods? — Carter Davis <theshakrah@...> 2008/11/10

Okay, I made an example.

[#320057] Convert text string i.e 'Peter' into integer ID — Justus Ohlhaver <ohlhaver@...>

Hello,

23 messages 2008/11/12

[#320101] Issue with block and getting to local variables — Tarek Other <cashew250@...>

Ok I'm new to ruby and want to do the following, I want to define a

12 messages 2008/11/12

[#320103] Need tutoring on using a path environment variable — dkmd_nielsen <donn@...>

I don't know what to do. I have an environment variable, PW_PATH,

11 messages 2008/11/12

[#320135] '#' characters are breaking my regexp — Max Williams <toastkid.williams@...>

I'm trying to build a regexp that includes music notes, eg Bb or C#.

14 messages 2008/11/13

[#320202] Highline - question with multiple choices — szimek <szimek@...>

Hi,

14 messages 2008/11/14
[#320208] Re: Highline - question with multiple choices — James Gray <james@...> 2008/11/14

On Nov 14, 2008, at 4:23 AM, szimek wrote:

[#320270] Re: Highline - question with multiple choices — szimek <szimek@...> 2008/11/15

On 14 Lis, 14:52, James Gray <ja...@grayproductions.net> wrote:

[#320213] Unit Conversion (#183) — Matthew Moss <matt@...>

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

37 messages 2008/11/14

[#320280] IO#lineno= doesn't work the way I expected — Chad Perrin <perrin@...>

I'm working on something that operates on each line of a file

22 messages 2008/11/15
[#320283] Re: IO#lineno= doesn't work the way I expected — Tim Hunter <TimHunter@...> 2008/11/15

Chad Perrin wrote:

[#320286] Re: IO#lineno= doesn't work the way I expected — Chad Perrin <perrin@...> 2008/11/15

On Sun, Nov 16, 2008 at 02:27:45AM +0900, Tim Hunter wrote:

[#320287] Re: IO#lineno= doesn't work the way I expected — "Michael Guterl" <mguterl@...> 2008/11/15

On Sat, Nov 15, 2008 at 2:08 PM, Chad Perrin <perrin@apotheon.com> wrote:

[#320288] Re: IO#lineno= doesn't work the way I expected — "Michael Guterl" <mguterl@...> 2008/11/15

On Sat, Nov 15, 2008 at 2:54 PM, Michael Guterl <mguterl@gmail.com> wrote:

[#320359] Why does tmail stop my CGI script form working? — Chad Perrin <perrin@...>

I decided to try TMail for the back end of a new contact page on a

14 messages 2008/11/17

[#320370] How can I overload a method in Ruby — Zhao Yi <youhaodeyi@...>

This is my class definition:

19 messages 2008/11/17
[#320374] Re: How can I overload a method in Ruby — Einar Magn俍 Boson <einarmagnus@...> 2008/11/17

there is no overloading, only overriding.

[#320417] How to extract links of a particular class type — "Sita Rami Reddy" <sitaramireddy@...>

I have a web page which has n number of links.

11 messages 2008/11/17

[#320446] function to select only certain key/value pairs from hash? — Aryk Grosz <tennisbum2002@...>

Whenever Im coding I usually come across having to create a new hash

14 messages 2008/11/17

[#320482] I don't like specs, should I change my point of view ? — Zouplaz <user@...>

Hello, I'm not trolling. I don't like specs (RSpec) : everytime I had a

18 messages 2008/11/18

[#320500] Should is the new Must? — Trans <transfire@...>

Why did 'should' become the going nomenclature of BDD framworks?

21 messages 2008/11/18

[#320553] Syntax question from a newbie to Ruby — David Spitzer <davidspitzer@...>

I am just learning Ruby and I can not seem to see why the first example

12 messages 2008/11/18

[#320655] build hash by iterating — Jason Lillywhite <jason.lillywhite@...>

I am building a hash this way:

15 messages 2008/11/19

[#320665] Question about host, gethostbyname and getaddress — Vladimir Fekete <fekete@...>

Hi *,

11 messages 2008/11/19

[#320709] ANN: One-Click Ruby Installer 186-27 Release Candidate 2 — Luis Lavena <luislavena@...>

Hello Ruby for Windows users!

11 messages 2008/11/19

[#320811] Found a ruby bug in the URI class, what do I do? — Ben Johnson <bjohnson@...>

I'm pretty sure this is a bug, and it seem so obvious that I'm thinking

9 messages 2008/11/20

[#320908] Befunge (#184) — Matthew Moss <matt@...>

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

28 messages 2008/11/22
[#321031] Re: [QUIZ] Befunge (#184) — Matthew Moss <matt@...> 2008/11/24

Hopefully the quiz isn't intimidating... It's a fairly simple language

[#321006] Can you run a command line script with arguments, without typing 'ruby' first? — "Jayson Williams" <williams.jayson@...>

Hi All,

29 messages 2008/11/24
[#321008] Re: Can you run a command line script with arguments, without typing 'ruby' first? — "Diogo Lisboa" <diogoslisboa@...> 2008/11/24

chmod a+x my_script (restrict permissions if you want)

[#321022] Re: Can you run a command line script with arguments, without typing 'ruby' first? — "Jayson Williams" <williams.jayson@...> 2008/11/24

I am using win os, so the shabang thing isn't an option for me. I put

[#321023] Re: Can you run a command line script with arguments, without typing 'ruby' first? — "Glen Holcomb" <damnbigman@...> 2008/11/24

On Mon, Nov 24, 2008 at 2:12 PM, Jayson Williams

[#321024] Re: Can you run a command line script with arguments, without typing 'ruby' first? — "Glen Holcomb" <damnbigman@...> 2008/11/24

On Mon, Nov 24, 2008 at 2:21 PM, Glen Holcomb <damnbigman@gmail.com> wrote:

[#321027] Re: Can you run a command line script with arguments, without typing 'ruby' first? — "Jayson Williams" <williams.jayson@...> 2008/11/24

The associations are correct. I reset them just to be sure though. I

[#321095] Re: Can you run a command line script with arguments, without typing 'ruby' first? — Daniel Schömer <daniel.schoemer@...> 2008/11/25

Jayson Williams wrote:

[#321037] Chris Pine tutorial assistance chapter 7 sort data without use of .sort method — jgheal@...

I'm learning to progam and came accross Chris Pine's Ruby Tutorial.

12 messages 2008/11/24

[#321039] Good math/stats libraries for Ruby? — Kenneth McDonald <kenneth.m.mcdonald@...>

There seem to be lots of small stats/math libraries for Ruby, but none

10 messages 2008/11/24

[#321166] time to back peddle? (Ruby 1.8.7) — Trans <transfire@...>

I just updated my Ubuntu system and was a bit surprised to find:

16 messages 2008/11/26

[#321179] How to get a reference to a block (when no explicit block parameter is used?) — Kenneth McDonald <kenneth.m.mcdonald@...>

In a function, I can find out if a block was given using block_given?,

8 messages 2008/11/26

[#321246] Performance issues with large files -- ruby vs. python :) — sa 125 <s_ayalon@...>

Hi all -

16 messages 2008/11/27
[#321248] Re: Performance issues with large files -- ruby vs. python :) — Florian Gilcher <flo@...> 2008/11/27

>

[#321271] Ruby's duck typing — "stephan.zimmer" <stephan.zimmer@...>

I would like to represent certain data by a list; to this end I let

17 messages 2008/11/27

[#321287] Programming Noob Chris Pine Tutorial sorting without use of array.sort method — whisperjim <jgheal@...>

I'm working through the following tutorial http://pine.fm/LearnToProgram/

10 messages 2008/11/27

[#321297] eRuby/erb outside of Rails — Jonny Noog <jonnynoog@...>

Hello,

18 messages 2008/11/28

[#321387] best gui toolkit — Warren Dulnuan <rr3800@...>

What is the best GUI toolkit for Ruby?

32 messages 2008/11/29
[#321397] Re: best gui toolkit — Vladimir Fekete <fekete@...> 2008/11/29

[#321421] Anyone scraping dynamic AJAX sites? — Becca Girl <cschall@...>

Hello.

12 messages 2008/11/30

[#321428] Enumerable#select used to return actual values — Mike Austin <"mike[nospam]"@...>

I'm pretty sure select used to use the actual value of the called block,

36 messages 2008/11/30
[#321432] Re: Enumerable#select used to return actual values — Robert Klemme <shortcutter@...> 2008/11/30

On 30.11.2008 04:46, Mike Austin wrote:

[#321906] Re: Enumerable#select used to return actual values — Mark Thomas <mark@...> 2008/12/04

On Nov 29, 10:46=A0pm, Mike Austin <"mike[nospam]"@mike-austin.com>

[#321912] Re: Enumerable#select used to return actual values — Trans <transfire@...> 2008/12/04

[SUMMARY] Unit Conversion (#183)

From: Matthew Moss <matt@...>
Date: 2008-11-21 19:44:00 UTC
List: ruby-talk #320880
> ## Unit Conversion (#183)


The right way, generally, to do a task such as unit conversion is to  
see if someone has already done all the hard work for you. As was  
pointed out, there are several options in this respect:

  * The [Stick] library for Ruby; a [brief summary] was provided.  
Stick provides a value class (i.e. quantity with units), conversions,  
syntactic sugar and more.
  * Google's search engine can act as a calculator, including unit  
conversions. Using Google's API is one option; another is screen- 
scraping, as was done by _Peter Szinek_. (Of course, as noted, you  
must have an activate Internet connection to use this solution.)
  * As was pointed out by _Ryan Davis_, there is a BSD/Un*x command  
and library called `units` which does this same task. Transform the  
arguments, pass them to a shell, and capture the output.

Many thanks to _Martin Boese_, whose solution had to be empirically  
confirmed. Repeatedly.

But I'm going to look at the solution from _Robert Dober_. While it is  
limited, as posted, his data driven approach could be expanded to  
include more conversions.

To understand how the expression `1.0.in.to.mm` will generate the  
string "25.4mm", I'll trace it a step at a time, looking at the  
relevant bits of code.

First, we have the float value `1.0`, but where does the method `in`  
come from? Clearly, class `Float` gets something by way of extension:

	class Float
	 include Conversion
	end

Module `Conversion` only defines one method that will extend `Float`  
(with the rest of `Conversion` being helper classes and code executed  
when `Conversion` is first evaluated). That method is `method_missing`:

	 def method_missing unit_name
	   pc = ProxyClasses[ unit_name.to_s ] || super( unit_name )
	   pc::new self
	 end

So we will look for `ProxyClasses["in"]` and, if not found, we just  
call to the parent class and hope it knows what to do with method call  
`in`. But in this case, we're expecting to find something in  
`ProxyClasses`... a Class, in fact, which we attempt to instantiate  
immediately using `new`. But where does we fill `ProxyClasses`?

Ah, that would be the code right below `method_missing` in his  
solution: the code that makes use of `LineParser`.

	conversions = LineParser::new
	File::open "units.txt" do | f |
	  f.each do | line |
	    conversions.parse_line line
	  end
	end
	
Robert provided a minimal `units.txt` data file to show how the code  
works. (Note that the line beginning "use SI" is part of the data file  
and not a mistake; see `parse_line` for how that is handled.)

	1 in = 0.0254 m
	1 l  = 0.001 m3
	use SI prefixes for m g l m3

It could be expanded greatly to support many more units. As each line  
is read, the `LineParser` object parses them, keeping track of the  
conversion rules -- I'll come back to that later. What I want to look  
at first is what gets done with those rules:

	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

`traverse` is going to enumerate over a number of valid conversions --  
source units, target units, and the conversion factor. And here we see  
from where the `ProxyClasses` originate... New `ProxyClass` objects  
are created through the code `Class::new ProxyClass` (but only if one  
didn't exist already for the particular source unit... note the use of  
the `||=` operator which only evaluates the right side and assigns  
left if the left was initially nil).

After ensuring that the `ProxyClass` corresponding to the source units  
exists, we call `module_eval` in order to add methods to the anonymous  
class just created. The method name will be the target units, and the  
method multiplies in the conversion factor, converts to a string, and  
appends the targets units.

So, getting back to our example `1.0.in.to.mm`, we've now found the  
`ProxyClass` corresponding to `1.0.in`. And we know that `ProxyClass`  
also has methods named by target units, which includes one that  
corresponds to the last part of the example: `.mm`.

If you're wondering about `to`, every `ProxyClass` defines that method  
to return self: essentially a useless function (in the sense that it  
does nothing more than `1.0.in.mm`). It's existence mimics other  
libraries, and the point is readability. (An alternative would be a  
more traditional call, such as 1.0.convert(:in, :mm) or similar.)

So once these proxy classes exist, there's very little effort going on  
to evaluate calls such as our example. And creating the proxy classes  
isn't much more difficult, assuming you have a proper conversion  
table. Now we come back to `LineParser` and what happens beyond its  
`parse_line` method. (I'll skip `parse_line` itself, since it is a  
few, simple regular expressions.)

Most of `units.txt` that defines our conversions is going to be  
handled by `add_conversion`, which just receives as arguments each  
split line of the data file. The conversion table (stored in `@c`) is  
two-layered hash -- a hash of hashes -- and is setup with this code:

	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

The conversion ratio (and the inverse conversion ratio) are stored in  
two places based on the indexing order. By storing both ratios/orders,  
we can convert in "both directions". That is, for our example, not  
only can we convert inches to millimeters, but millimeters to inches.

The last bit of file parsing is adding appropriate metric prefixes (SI  
units). One line in the file indicates which units are worthy of  
metric prefixes. In the data file provided, we see that meters can  
accept metric prefixes (such as "kilo" and "milli"), but inches will  
not. These prefixes are handed by `add_si_unit_for`:
	
	def add_si_unit_for unit
	  SIUnits.each do | prefix, conversion |
		@c[ prefix + unit ][ unit ] = conversion
		@c[ unit ][ prefix + unit ] = 1 / conversion
	  end
	end
	
Here, `unit` is the particular unit we want to support metric  
prefixes. `SIUnits` is the hash containing the metric prefixes as  
characters and the corresponding orders of magnitude. For every unit  
and metric prefix, two more conversions are added, each the inverse of  
the other: conversion between the naked unit and the adorned unit  
(e.g. between meters and millimeters, and vice-versa).

Finally, `traverse` is an enumerator that will yield (via `blk.call`)  
every valid combination of units and the appropriate conversion  
factor. It manages this without storing every conversion (e.g. we  
store the inches to meters conversion, and the meters to millimeters  
conversion, but don't explicitly store inches to millimeters).  
Enumerating every possible, valid conversion is done in the private  
method `_traverse`:

	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

The final, recursive step here is what allows us to build a transitive  
closure of all units. `src_unit` is, of course, the source unit (e.g.  
inches). `unit_conversion` contains all possible immediate conversions  
from the source and is the hash of units and conversion factors. And,  
you can see, we enumerate those into `new_unit` and `conversion`.

We skip a target unit if it's already been visited (i.e. in  
`traversed_units`). Otherwise, we yield to the caller (`blk.call`) and  
recurse, now converting the source unit to everything the target unit  
can also be converted, making sure to update `traversed_units` so as  
to terminate eventually.



[1]: http://stick.rubyforge.org/
[2]: http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/320583


In This Thread

Prev Next