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

This is a simple example lifted from a book. It is supposed to trigger the redo statement. Look perfectly ok to me but when i run it, it accepts number greater than 9. What could be wrong?
print "** Random 4 Lotto Numbers **\n"; print "** Type any four digits (0 to 9) **\n"\n; for ($i=1; $i <= 4; $i++) { print "Choice $i: "; chomp ($digit[$i] = <>); redo if $digit[$1] > 9; redo if $digit[$1] < 0; } print "Your choices :@digit \n";

Replies are listed 'Best First'.
Do not use diamond for STDIN
by merlyn (Sage) on May 02, 2001 at 17:58 UTC
    Besides the points made in Re: Loop Control : REDO, you should never use the diamond with interactive I/O like prompting. Use <STDIN> instead. Otherwise, your day will be very miserable when someone types a filename on the command line. You'll still get all those prompts, and it'll look very funky.

    As an alternative, say print ... if $ARGV eq '-', then you'll only be prompting if you're actually reading from STDIN. The output-only results printing can be unconditional.

    -- Randal L. Schwartz, Perl hacker

      Sir,
      It seems like this may be a very desireable action at times. EG. in the case where you want a script to be interactive, but also have it cronned with all of the values for it in another file. In which case <> will do exactly what you want in both cases. Isn't this more a question of style that what SHOULD/SHOULDN'T be done?

      -Fatty Lumpkin

      Update: Merlyn is 1000% correct, poor reading on my part, i missed the actual printed prompt.I am just used to doing something like
      shift || (print "\nprompt:" and <>);

      But that doesn't allow for this behavior either.I missed the point, Thanks for correcting me.
        It seems like this may be a very desireable action at times. EG. in the case where you want a script to be interactive, but also have it cronned with all of the values for it in another file. In which case <> will do exactly what you want in both cases.
        In which case, you don't want to prompt, because then you get a bunch of prompts in the output, without responses. That... looks... tacky. I'd fatal that in a code review.

        -- Randal L. Schwartz, Perl hacker

Re: Loop Control : REDO
by suaveant (Parson) on May 02, 2001 at 17:40 UTC
    You are gonna smack yourself... you put a $1(one) instead of $i in your checks...

    It works then but allows letters... maybe wrap it in an int()?

    Update Wrapping it in an int makes letter entries zero, maybe instead of chomp do

    #!/prod/gnu/bin/perl print "** Random 4 Lotto Numbers **\n"; print "** Type any four digits (0 to 9) **\n\n"; for ($i=1; $i <= 4; $i++) { print "Choice $i: "; ($digit[$i] = <>) =~ s/\D//g; redo unless $digit[$i]; redo if $digit[$i] > 9; redo if $digit[$i] < 0; } print "Your choices :@digit \n";
    then you just get the numbers.
                    - Ant
      Thanks suaveant for spotting the typo. The example came in the cd but i didn't spot that.
      And if you're going to test the input with a regular expression, then use that on the redo line:
      print "** Random 4 Lotto Numbers **\n"; print "** Type any four digits (0 to 9) **\n"; for ($i=1; $i <= 4; $i++) { print "Choice $i: "; chomp ($digit[$i] = <STDIN>); redo if $digit[$i] !~ /^\d$/; } print "Your choices :@digit \n";

      iakobski

        yours is good, but why chomp? no need with that regexp...
        print "** Random 4 Lotto Numbers **\n"; print "** Type any four digits (0 to 9) **\n"; for ($i=1; $i <= 4; $i++) { print "Choice $i: "; redo if ($digit[$i] = <STDIN>) !~ /^\d$/; } print "Your choices :@digit \n";
        since the $ works fine with \n there
                        - Ant
(boo)Re: Loop Control : REDO
by boo_radley (Parson) on May 02, 2001 at 18:24 UTC
    Although I imagine that what you're reading is probably just trying to explain loops in perl, that's probably not the best way to do things...
    use strict; print "** Random 4 Lotto Numbers **\n"; print "** Type any four digits (0 to 9) **\n"; my @digit; foreach my $i (1..4){ while ($digit[$i] =~/^[\D]$/){ print "enter digit $i : "; chomp($digit[$i] =<>)}; } print "Your choices : @digit \n";
    As an aside, I thought that redo and friends were only applicable inside of while or foreach loops, but apparently, that's not the case. Hm!
      It is good to point out that use strict is needed here.

      If you also add use warnings, you will notice that the print statement is accessing an undefined value,  $digit[0] I suspect that the original author meant to say the equivalent of

      foreach my $i ( 0..3){
      or if not the print should be:
      print "Your choices : @digit[1..4] \n";

      iakobski

      From Programming Perl, 3rd Edition Page 118
      The foreach keyword is just a synonym for the for keyword, so you + can use foreach and for interchangeably...

                      - Ant
        well, YES, that is true.
        however, from perl's documentation on continue :
        If there is a continue BLOCK attached to a BLOCK (typically in a while or foreach)...

        which made me believe you could tack continues on to any block all willy nilly. This isn't the case, though.