could someone please explain if the infinite "while (1)" loop is required for this?
To really see why an infinite while() loop was used, let's try re-writing the code to remove or replace that loop.
In all of the code below, we will assume that the user already knows to just press ENTER to exit.
Here is the original code, shrunk to the minimum needed to illustrate the issue:
while (1) {
print 'Please enter a word: ';
chomp(my $word = <STDIN>);
last if $word =~ /^\s*$/;
print "You entered '$word'\n";
}
If we did not need to prompt for input, and STDIN was magically auto-chomping, we could use a while(my $word=<STDIN>){...} construct.
Since neither of those is true, what *can* we put in the while() condition to make it finite?
Let's try checking for the empty $word in the while() condition:
# Had to duplicate the prompt, <read>, and chomp.
# Duplication is bad!
# Use the DRY principle: [D]on't [R]epeat [Y]ourself!
my $word;
print 'Please enter a word: ';
chomp($word = <STDIN>);
while ( $word !~ /^\s*$/ ) {
print "You entered '$word'\n";
print 'Please enter a word: ';
chomp($word = <STDIN>);
}
Let's try that again, but not do any work outside the loop:
# Had to add `not defined $word` to handle the special case
# during the first time through the loop. Very Ugly!
# Had to duplicate code `$word !~ /^\s*$/` to prevent the
# final ENTER from printing "You entered ''" as it exits
# the loop.
my $word;
while ( ( not defined $word ) or ( $word !~ /^\s*$/ ) ) {
print 'Please enter a word: ';
chomp($word = <STDIN>);
if ( $word !~ /^\s*$/ ) {
print "You entered '$word'\n";
}
}
Maybe initializing $word will help:
# Instead of using `not defined $word`, I force
# non-whitespace into $word.
# Still very ugly.
# Still had to duplicate code `$word !~ /^\s*$/`
my $word = 'JunkToPreventFailingTheFirstLoop';
while ( $word !~ /^\s*$/ ) {
print 'Please enter a word: ';
chomp($word = <STDIN>);
if ( $word !~ /^\s*$/ ) {
print "You entered '$word'\n";
}
}
Invert the while() loop into a do...while loop?
# Still had to duplicate code `$word !~ /^\s*$/`
my $word;
do {
print 'Please enter a word: ';
chomp( $word = <STDIN> );
if ( $word !~ /^\s*$/ ) {
print "You entered '$word'\n";
}
} while $word !~ /^\s*$/;
We *can* use a finite while() loop by moving the prompt/<read>/chomp into a sub-routine.
sub prompt {
my ($prompt_string) = @_;
print $prompt_string;
my $input = <STDIN>;
chomp $input;
return $input;
}
# Success!
while ( my $word = prompt('Please enter a word: ') ) {
print "You entered '$word'\n";
}
Since that prompt() sub would be useful in many different programs, it should be turned into a module.
TheDamian already did, and his IO::Prompt module is on CPAN.
use IO::Prompt;
while (my $word=prompt 'Please enter a word: ', -while => qr/\S/) {
print "You entered '$word'\n";
}
I would conclude that the Llama's use of the infinite while() loop is the clearest option that does not involve a prompt() sub or module.
|