Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery
 
PerlMonks  

Re: a hash and 2 loops--not working

by davido (Archbishop)
on Nov 08, 2012 at 23:48 UTC ( #1003027=note: print w/ replies, xml ) Need Help??


in reply to a hash and 2 loops--not working

IO::Prompt::Hooked handles the details of prompting, detecting an interactive environment, and accepting callbacks to validate user input and to issue meaningful warnings if the user input is invalid. It continues to loop upon invalid input until it gets good input, or until an optional "tries" count reaches zero. Here's an example of how it could be used for your project.

use strict; use warnings; use Scalar::Util qw( looks_like_number ); use IO::Prompt::Hooked; my %rates = ( 'pounds' => 1, 'usd' => 1.6, 'marks' => 3.0, 'french francs' => 10.0, 'yen' => 174.8, 'swiss francs' => 2.43, 'drachma' => 492.3, 'euro' => 1.5 ); my ($from, $to) = get_targets(\%rates); my $value = get_value(); my $converted = $value * ($rates{$to} / $rates{$from}); printf "$value $from is %.2f $to.\n", $converted; # Manage prompting for "from" and "to" currencies. sub get_targets { my $currencies = shift; return map { get_currency($_, $currencies) } ('Enter your starting currency:', 'Enter your target currency:'); } # Prompt for an individual currency. 5 tries. Die if we don't get # good input. sub get_currency { my ($msg, $curr) = @_; my $in = prompt( message => $msg, tries => 5, validate => sub { return exists $curr->{lc shift}; }, error => sub { my $valid = join ', ', keys %{$curr}; return "Currency must be one of the following: ($valid).\n" . "$_[1] tries remaining.\n"; }, ); die 'Too many tries. Consult someone who can read.' unless defined $ +in; return lc $in; } # Prompt for a value to convert. Die if we don't get good input after # five tries. sub get_value { my $input = prompt( message => "Enter an amount to convert:", default => 0, tries => 5, validate => sub { return looks_like_number shift; }, error => sub { return "Amount must be a number. $_[1] tries remaining.\n"; }, ); die "Unable to obtain a valid value for conversion." unless defined +$input; return $input; }

Here we prompt for a "from" and a "to" currency. We provide the user five tries at getting it right, and upon receiving invalid input, we prompt with additional information enumerating the valid inputs. If after five tries the user can't figure out how to get it right, we die. Then we move on to getting a value, and again loop until we actually get a number (or until the allotted number of tries expires). Finally, we perform the conversion, and output it, rounded to two decimal places.

This is sort of a shameless plug for the module. What I like is that it handles the logic and looping. All the module's user has to do is provide callback subs that perform the validation, and that provide an error string in case of bad input. The error string could also be a simple string instead of a subref, but the subref provides the opportunity to output the number of remaining tries. The module is based on IO::Prompt::Tiny, but while it's still smaller and easier to use than the granddaddy, "IO::Prompt", it's not tiny either. ;)

As for how this example code turned out: You might say, "But it's bigger than the original code." ...Yes, it is. But it is doing more; it's validating both the targets and the numeric values, it's providing meaningful messages for invalid input, and it's dieing if the user can't seem to figure out how to provide valid input after a reasonable number of tries.

Update: Changed all hash keys to lower case per frozenwithjoy's observation. Output will be 'usd' instead of 'USD'. Does it matter? ;)


Dave


Comment on Re: a hash and 2 loops--not working
Download Code
Re^2: a hash and 2 loops--not working
by frozenwithjoy (Curate) on Nov 09, 2012 at 00:04 UTC
    I like this; however, USD isn't working for me (presumably because of the lc in validate => sub { return exists $curr->{lc shift}; },

    Edit: I fixed it by changing that line to:

    validate => sub { my $chosen_curr = shift; return exists $curr->{$cho +sen_curr}; },

    And by removing lc from return lc $in;

      Good call. :) It might be better to just change the hash so that all the keys are lower cased. I'll update.


      Dave

Re^2: a hash and 2 loops--not working
by jhumphreys (Novice) on Nov 09, 2012 at 16:58 UTC

    Dave-

    Thanks for your feedback. Obviously, I'm just beginning in Perl. But will study.

    Best,

    -j

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://1003027]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chanting in the Monastery: (11)
As of 2014-07-28 13:06 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    My favorite superfluous repetitious redundant duplicative phrase is:









    Results (198 votes), past polls