Beefy Boxes and Bandwidth Generously Provided by pair Networks
We don't bite newbies here... much

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? ;)


Replies are listed 'Best First'.
Re^2: a hash and 2 loops--not working
by frozenwithjoy (Priest) 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.


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


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



Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://1003027]
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others exploiting the Monastery: (1)
As of 2017-04-29 17:37 GMT
Find Nodes?
    Voting Booth?
    I'm a fool:

    Results (533 votes). Check out past polls.