in reply to The Definitive Unit Conversion Script
Carter's compass: I know I'm on the right track when by deleting something, I'm adding functionality
|Replies are listed 'Best First'.|
Re^2: The Definitive Unit Conversion Script
by Aristotle (Chancellor) on Dec 13, 2002 at 19:22 UTC
Thanks for the compliment :)
Anyone have suggestions how I should go about modularizing this?
At first I didn't see any way to meaningfully transform it into a module since it has to have a useful interface. Then I began thinking.. the following is a brainstorming transcript if you will, so bear with me and give me feedback.
I guess I should swap out the inner loop into of the script into a subroutine and package it with the table. The rest of the hoohah for calling the script from the commandline could ship as a script like the GET, HEAD etc ones that come with LWP. The inner loop alone probably offers too little control - extra routines to allow to ask for specific conversions should be useful. So I should probably add an interface to ask whether a unit is known. Actually, the best way to do that is probably to have an interface that returns all possible conversion targets for a given unit, which would obviously return nothing (= false in scalar context) if the unit is unknown.
The question of whether there should be an interface to add new conversions at runtime and how it should look repeatedly crossed my mind during all this, but I don't feel like it's a direction I'd want to take the module in. It's probably better if it remains something a.. "metaconstant" module, a bunch of oft needed calculations you just drop in your script and don't think about. It almost makes me think the "introspection" interface (requesting what conversion targets exist for a given unit) is overkill, but then it is probably useful even for static uses for things like maybe dynamically populating dropdowns or such.
If I'm gonna go there, maybe there should plaintext names and/or descriptions for the units supported.. on second thought, that would require translation and all the countless headaches it brings along, which is definitely farther out than I want to go. It would require an extra interface for the language selection and querying the available languages too, and people will probably still have to reimplement those themselves if it doesn't happen to support their language. If could include a lot of languages - but neither do I know who I'd ask for translations, nor would I be willing to put in the effort to maintain all of that. And it would probably be useless bloat for the vast majority of users. Maybe as an addon module ::Descriptions or something, should the interest in this module ever warrant that amount of work.
So I have a module containing the conversion table, a routine for one-shot conversions, one for broadside salvo conversions (calculate any, all and every related unit you can get to), and one to ask whether a unit is known and what conversion targets it has, if so.
Then the query routine should probably differentiate between all available direct conversion targets that can be reached via the one-shot routine and the full list of related units you can get via the broadside converter.
Maybe there should be a single unit to single unit conversion routine which does not care whether a direct conversion is possible or intermediate conversions have to be done. But that would be complex - choosing the conversion steps such that you get from the source to the destination in the shortest possible way - or even at all - is far from trivial. It is simpler to just bruteforce a broadside conversion and pluck the result out of it. But the user can do that too, esp if the broadside conversion function returns its results in a convenient format. There's no point in adding garden decoration to the module.
The most convenient format is probably to return either a hashref or flat list according to the context.
Ok, I'm done. Suggestions, anyone?
Makeshifts last the longest.
The interface I would want would be something like:
my @new= ConvertTo( $toUnits, $fromUnits, @old );
Note that it is "to" followed by "from" so the unit name is closest to the numbers that are/will be in those units.
If I want to do a lot of conversions but not all at once, then:
And I'd probably support aliases for units and include a "long alias" for every unit: "inch", "feet", "meter", "centimeter", "Centigrade", "Farenheit", "Kelvin", "second", "hour", etc. just to avoid confusion.
I'd probably put the unit data after __DATA__ so you could just append more units to be able to support them.
The "all possible conversions" is interesting for interactive exploration, but I don't see nearly as much use for it as requesting a specific conversion.
For finding the conversion, I'd look for a direct conversion, but if there isn't one, form the list of all possible conversions and then repeat for each of those:
Untested (I hope you don't mind). - tye
I haven't looked at your code in detail yet, nor tried to use the interface you've got already, specifically so that I would not be influenced.
My first thought on how I would like to use a conversions module is that I would pass the source and destination units and it would create a named sub in my package namespace (like use constant does).
In use, it might look something like this:
Final thought on the precision and under/overflow thing. Perhaps, if a flag is set, the routines could return BigInt/Floats if the standrad precisions will cause accuracy loss? I haven't thought that through, so I don't know what the implications are.
Now I'll read your code and see if I'm completly off-base, but I like to look at things from my own perspective first when I can :^).
If you decide not to go ahead with teh module, let me know and I will.
Examine what is said, not who speaks.
Makeshifts last the longest.
How about putting the info from @tables into an XML format and then read it in when the module is loaded? You could also add an option to update the XML tables from an internet site, if needed.
Sounds like an interesting idea, but on second thought, that requires the module to be XML aware and network aware. I don't think these things belong into it; if I go there, I rather offer a clean interface for updating the @table and let the module user do the rest. Adding a sample script to the distribution that uses that interface to import an XML table might be a nice idea though.
Update: Actually, that sounds like a very nice idea. The sample script could import currency data from somewhere. Of course, it should carry a disclaimer, since currency calculations should not be done using floating point math when actual money depends on them, but it would still be a very nifty demonstration of the module's design.
Makeshifts last the longest.