irvson has asked for the wisdom of the Perl Monks concerning the following question:

I'm new to Perl. (I prefer "neophyte" to "newbie.") I'll post the code, then tell you what I'm trying to do.

do { print "\nPick a letter between a and d.\n "; chomp($reply = <STDIN>); } while (lc $reply ne ('a' .. 'd'));

I want the user to answer multiple choice questions by choosing the letters "a" thru "d." If any other letter is entered, I want the script to show the first "print" line. Once a letter "a" thru "d" is entered, I want the script to continue to the question. I tried "while" and "if" loops (without "do"), and I ended up with a loop that just kept repeating "Pick a letter between a and d." Using "do" -- as shown -- has, at least, created the situation where the "Pick a letter..." sentence appears once, and then the script waits for another input. BUT...even when I enter a letter "a" thru "," the script keeps repeating "Pick a letter..." I've gotten the impression that the range operator works only with arrays, which is not the case here.

So, I have two questions: 1) How do I specify that the input must be in the range "a thru d?" 2) How do I get the script to move on to the question once an accepted letter has been entered.

Thanks.

Replies are listed 'Best First'.
Re: do-while loop
by davido (Cardinal) on Nov 03, 2009 at 17:09 UTC

    You're misusing the range operator. You probably want while( $reply !~ /^[abcd]$/i );, which is to say, while $reply does not contain the single character of a, b, c, or d, case insensitive.

    I don't really love the loop you've chosen either. I think I would be more comfortable with:

    prompt; while( <STDIN> ) { chomp; last if /^[a-d]$/i; prompt; } sub prompt { print "\nPick a letter between a and d.\n"; }

    The inelegant part is that you have to prompt outside of the loop once, but the more elegant part is that you're actually checking whether or not there IS more input to be read. That way, should STDIN happen to run out of input (for example, in the case of the script being fed an input file instead of keyboard input, it can terminate when it runs out of lines of text to read.


    Dave

      The inelegant part is that you have to prompt outside of the loop once, but the more elegant part is that you're actually checking whether or not there IS more input to be read. That way, should STDIN happen to run out of input (for example, in the case of the script being fed an input file instead of keyboard input, it can terminate when it runs out of lines of text to read.

      I think that one could strike a balance by using

      do { … } while length $reply and …
      (This quits on empty input; but, then again, the original version provides no non-CTRL-C way to get out of the loop if you don't happen to remember the letters from a to d—so maybe that's a feature.)

      UPDATE: Oops, my original version had while $reply and …, which quits on input 0, which seems incorrect by any measure.

Re: do-while loop
by kennethk (Abbot) on Nov 03, 2009 at 17:14 UTC
    One way you can get code to do what you want would be with:

    #!/usr/bin/perl use strict; use warnings; print "\nPick a letter between a and d.\n"; chomp(my $reply = <STDIN>); while ($reply !~ /^[a-d]$/i) { print "Please enter only a letter between a and d.\n"; chomp($reply = <STDIN>); }

    This code will stay in that while loop until it gets an acceptable response. A couple notes on how it works:

    1. Note the use strict;use warnings at the top of the script. If you are not familiar with these, I would suggest you become so, since they can save you a good bit of debugging headache.
    2. In order to check the input, I use a regular expression. The pattern starts at the beginning of the expression (^), matches one character between a and d inclusive ([a-d]) and then must stop at the end of the expression ($). The modified i makes the match case insensitive. The binding operator !~ is the 'not-matching' operator, so if the contents of $reply do not match the pattern, you stay in the while loop. See perlre and perlretut for info on regular expressions.
    3. In your original code, you use the scalar operator ne on a list. This puts the list in scalar context and hence returns the length of the list, not a comparison against elements. You would only exit, therefore, when $reply was '4'.

    Hope this clears things up. Welcome to the monastery.

Re: do-while loop
by CountZero (Bishop) on Nov 03, 2009 at 21:59 UTC
    If you are using ActiveState Perl, have a look at the module ActiveState::Menu. It will do exactly (or even more) than you require.
    use strict; use ActiveState::Menu qw(prompt); my $answer = prompt( 'Pick a letter between a and d.', 'must_match' => + qr/[abcd]/ ); print "Your answer was $answer\n";

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

      Thank you all for the help. I will get to work.

      J...
Re: do-while loop
by Anonymous Monk on Nov 03, 2009 at 17:06 UTC