The Author Online Book Forums are Moving

The Author Online Book Forums will soon redirect to Manning's liveBook and liveVideo. All book forum content will migrate to liveBook's discussion forum and all video forum content will migrate to liveVideo. Log in to liveBook or liveVideo with your Manning credentials to join the discussion!

Thank you for your engagement in the AoF over the years! We look forward to offering you a more enhanced forum experience.

import-bot (20211) [Avatar] Offline
[Originally posted by paulbeard]

OK, somehow I misread the spec for question 5.6.3: I added a converter for
temps as well (no, I don't know why).

Now that I have it, I'm curious how else it could be done. This works but
lacks elegance, even by perl's ungainly aesthetic standards. And yes, I know
there's not tests for valid input. I'd have that if I hadn't spent so much
time scratching my head over temp conversions.


use strict;
my ($typeOfConversion, $units, $input, $output, $label);
print STDOUT "Are we converting lengths or temperatures? [ l for lengths | t
for temps ]";
chomp ($typeOfConversion = <STDIN>smilie;
print STDOUT "Input will be in: [ e for english | m for metric ]? ";
chomp ($units = <STDIN>smilie;
print STDOUT "What value to convert? ";
chomp ($input = <STDIN>smilie;
if ($typeOfConversion eq "l" and $units eq "e" ) {
$label = " centimeters.";
$output = $input * 2.54;
elsif ($typeOfConversion eq "l" and $units eq "m" ) {
$label = " inches.";
$output = $input / 2.54;
elsif ($typeOfConversion eq "t" and $units eq "e" ) {
$label = " degrees C.";
$output = ($input -32) / 1.8;
else {
$label = " degrees F.";
$output = ($input * 9/5) + 32;
print "$output $label
import-bot (20211) [Avatar] Offline
Re: d'oh! do I get extra credit?
[Originally posted by jandrew]

Well, first I'd like to say well done -- and I'm glad to see that
you combined the tests so you didn't have to resort to nesting your
conditional tests.

At the point we are at in the book, there isn't a whole lot that can
be done to clean up the code. You can drop the explicit use of STDOUT
for one thing. And, given the simple nature of the conditional
inputs, you could combine the conversion type and units into a single
string and then not need combined tests in the conditionals. You could
also concatenate the output labels directly to the calculated values
if you wanted as well:

#!/usr/bin/perl -w
use strict;

print "Convert [t]emperatures or [l]engths? [t/l]: ";
chomp(my $type = <STDIN>smilie;

print "Input is [e]nglish or [m]etric? [e/m]: ";
chomp(my $units = <STDIN>smilie;

print "Enter a value to convert: ";
chomp(my $input = <STDIN>smilie;

my $conversion = "$type$units";
my $output;

if ($conversion eq 'le') {
$output = ($input * 2.54) . ' centimeters';
elsif ($conversion eq 'lm') {
$output = ($input / 2.54) . ' inches';
elsif ($conversion eq 'te') {
$output = (($input - 32) * 5/9) . ' degrees C.';
elsif ($conversion eq 'tm') {
$output = (($input * 9/5) + 32) . ' degrees F.';
else {
die "Unrecognized conversion '$type' or units '$units'

print "$output

There are ways to avoid long series of sequential if/elsif in certain
cases using hashes -- consider this code:

#!/usr/bin/perl -w
use strict;

print "Convert temperatures or lengths? [t/l]: ";
chomp(my $type = <STDIN>smilie;

print "Input is english or metric? [e/m]: ";
chomp(my $units = <STDIN>smilie;

print "Enter a value to convert: ";
chomp(my $input = <STDIN>smilie;

my %convert_map = (
le => ($input * 2.54) . ' centimeters',
lm => ($input / 2.54) . ' inches',
te => (($input - 32) * 5/9) . ' degrees C.',
tm => (($input * 9/5) + 32) . ' degrees F.',

my $conversion = "$type$units";

unless (exists $convert_map{$conversion}) {
die "Unrecognized conversion or unit

print "$convert_map{$conversion}

Now, this essentially pulls the repetive nature of the if/elsif tests
out and replaces it with a hash mapping -- we've turned the series of
tests into a simple hash look-up. The bad thing about the above is
that we wind up actually calculating a bunch of unnecessary (and
meaningless) values when we only want one -- we do *not* actually want
to do it this way.

But if we could delay calculating those expressions in %convert_map
until we needed one we would be better off -- and in fact, using
references to subroutines (or anonymous subroutine references) we can
do exactly that and build our hash like this:

my %convert_map = (
le => sub {($_[0] * 2.54) . ' centimeters'},
lm => sub {($_[0] / 2.54) . ' inches'},
te => sub {(($_[0] - 32) * 5/9) . ' degrees C.'},
tm => sub {(($_[0] * 9/5) + 32) . ' degrees F.'},

Then, we'd change our final print statement to this:

print $convert_map{$type}->($input),"

And the rest of the code is unchanged.

Now, this is definitely more advanced (chapters 7 and smilie than
we are at for this exercise -- all I was looking at this point
was simply obtaining user input and then acting conditionally
on that input (which you achieved perfectly well with more
conditions than the exercise called for).

I just wanted to point out the hash-mapping technique because it can
be quite useful (even when subroutine references aren't needed).
Consider a mapping of monthname abbreviations to full monthnames:

my %months = (
Jan => 'January',
Feb => 'February',
# etc.

# now somehow you have input in abbreviated form (maybe you
# split up a date string into various parts and the month was
# abbreviated) but you want to output fullnames:

my $month = 'Feb'; # wherever this came from
print "$months{$month}

I hope this little sidebar discussion has been helpful (or at least
provides a taste of things to come).