Re: Modifying loop structure and placement
by merlyn (Sage) on Dec 24, 2009 at 02:05 UTC
|
Are you serious? This is one of the exercises in JLFord's book? This is exactly the same exercise rootbeer created for our Learning Perl book.
Of course, it's not that unobvious of a question, but this now seems suspicious. Do you have a copy of Learning Perl to compare some of the other exercises for me? I'm curious now.
-- Randal L. Schwartz, Perl hacker
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.
| [reply] |
|
|
Yes, the Ford script does the same thing, but it's much more involved -- about 175 lines of code. Longer doesn't mean better, of course. I found your script and created the PL file. Much simpler, does the same thing, but uses a regular expression -- which Ford hasn't covered at that point in his book. There sure is a lot to learn here, and comparing the two scripts for "economy" and "verbosity" is just one of those things.
| [reply] |
|
|
(Oops. Forgot the second part of your question.) I have both books. I'll look for other similarities. Altho your code in this example is so completely different from Ford's that I would not have noticed the similarity right away. I'm just getting started with your book. I'll keep my eyes peeled. (You would probably satisfy your curiosity more quickly by buying a copy of his book -- or getting it from a library. He constructs each chapter so that the script he uses at the end of the chapter sums up what he presented in that chapter.)
| [reply] |
Re: Modifying loop structure and placement
by keszler (Priest) on Dec 24, 2009 at 01:53 UTC
|
One possibility is the redo command. Add a LABEL at line 4 (or 1) and replace lines 10 and 11 with redo LABEL. | [reply] [d/l] |
Re: Modifying loop structure and placement
by AnomalousMonk (Archbishop) on Dec 24, 2009 at 14:43 UTC
|
| [reply] [d/l] [select] |
|
|
I don't know what "factor common sub-statemnts" means.
In several of these exercises I have, in fact, re-worked code, but I'm not ready for any extensive re-working.
Your last suggestion about using "readmore" is certainly worth considering for some posts. I understand what you're saying. But I'm still at the early learning stage, and posting this longer, non-executable code block allowed me to present a specific problem. When I've learned more, I hope I will be able to follow your suggestion.
| [reply] |
Re: Modifying loop structure and placement
by ambrus (Abbot) on Dec 24, 2009 at 14:47 UTC
|
| [reply] |
Re: Modifying loop structure and placement
by biohisham (Priest) on Dec 24, 2009 at 16:12 UTC
|
(#print "Enter your guess: \n\n > "; ">" in book's script; why?)
Just to provide a prompt for entering a number, ">" can be "%" or any other character for that matter, like you don't want the user to think that the scrip is hanging or something...
Now to return to line 4 like you want just use a label, a label is used in conjunction with goto, so the first segment of your code can be re-written as:
until ($isover) {
clear_the_screen();
print "Find the secret number between 1 and 100: \n\n";
LABEL: print "Enter your guess: \n\n % "; #">" #in book's scrip
+t; why?
chomp($userguess = <STDIN>);
#following "while" loop added by me in a "challenge"
while ($userguess !~ /^[+-]?\d+$/ ) { #testing to see whether user
+ entry is a number
print "\nYour guess must be a number. Try again. \n\n";
goto LABEL;
}
Notice, I did not use "chomp($userguess = <STDIN>);" in line 11 since the flow would go from 4 four onwards once again...
I'd stress on using the pragmas:
use strict;
use warnings;
these can be very helpful at ensuring that you don't mistype a variable name and can give you helpful warnings and thus greatly reduce the debugging time..
with $totalguesses, you can't return outside a subroutine, eval statement or do{}, hence using a print statement instead...
("if" loop beginning on line 17, pressing <Enter> after the 3 "elsif" choices returns the code to line 4)
Not entirely correct, what returns the code to line 4 is that the $isover in until(){} is not defined yet and hence this observation...
There's an extensive repetitive writing of certain statements in your code, that warrants taking a look at Perl subroutines which can enhance the logic-flow interpretation of the code..
Update: I did not mention that I pre-assigned $secretnumber with the value of 100....
Here's your code once again with places for you to notice being commented accordingly:
use strict;
use warnings;
my $isover; #using strict requires variables declaration wit
+h 'my'.
my $totalguesses;
my $reply;
until ($isover) {
clear_the_screen();
print "Find the secret number between 1 and 100: \n\n";
LABELED: print "Enter your guess: \n\n % "; #labeling a posi
+tion
my $userguess;
chomp($userguess = <STDIN>);
while ($userguess !~ /^[+-]?\d+$/ ) {
print "\nYour guess must be a number. Try again. \n\n";
goto LABELED;
}
$totalguesses++;
my $secretnumber=100;
if ($userguess == $secretnumber) {
clear_the_screen();
print "You guessed it! Press <Enter> to return to the Main Me
+nu.\n\n";
chomp($reply = <STDIN>);
$isover = 1; #Defines $isover hence the evaluation would retu
+rn true
#the until(){} block would exit then...
}elsif ($userguess > 100) {
print "\nYou chose a number higher than 100.<Enter> to try aga
+in.\n";
chomp($reply = <STDIN>);
}elsif ($userguess < $secretnumber) {
clear_the_screen();
print "$userguess is too low. Press <Enter> to guess again.\n\
+n";
chomp($reply = <STDIN>);
}elsif ($userguess > $secretnumber) {
clear_the_screen();
print "$userguess is too high. Press <Enter> to guess again.\n
+\n";
chomp($reply = <STDIN>);
}
}
print "your guesses $totalguesses\n";
sub clear_the_screen{
}
Finally, checking the Perl Documentation at the website or through your console is so rewarding so make it your new-year's new habit :).... Best of luck :)
Excellence is an Endeavor of Persistence.
Chance Favors a Prepared Mind.
| [reply] [d/l] [select] |
|
|
| [reply] |
|
|
<= and >= are used in the script I copied from Ford's book. And now that you point this out, using that code doesn't make sense. That's just basic math structure, and even this Perl neophyte knows that. I will have to pay closer attention to the script I copy from his book. Thanks.
| [reply] [d/l] [select] |
|
|
| [reply] |
|
|
Another "oops." The above reply is to the post above yours.
| [reply] |
|
|
First reply to the post below yours was meant as a reply to your post. (I didn't aim too well.)
| [reply] |
Re: Modifying loop structure and placement
by Marshall (Canon) on Dec 24, 2009 at 18:21 UTC
|
I don't know about these various "challenges". But it appears to me that some basic "command line loop" hints are in order.
There are three things that we want to accomplish in the first statement of the user command loop:
1. prompt the user
2. get some user input
3. decide whether we should stop the loop (like a quit, QuIt command or whatever).
In my opinion the most common condition that "ends the loop" should be apparent right in the looping statement, not buried later down as some "last" in the body. Of course with all programming things there are judgments and I do use "last".
This is one of the few places where the "comma" operator is the right idea! If you look at my code below, the while() statement contains all three of the above things! We prompt the user, then get the input and check it against the "quit" command in various cases. The truth or falseness of the while() is determined by the last statement in the while. The use of the comma operator here also eliminates the use of multiple print statements to re-prompt the user. There are cases where "retry" is appropriate, but I don't think that this is one of them.
So now that we have prompted the user and gotten some input and decided that the program should continue, the user input data validation starts.
The first thing is that a blank line should do "nothing" except re-prompt. That's just like your OS does when you hit "enter" or "carriage return"! This is expected behaviour.
Now we proceed to perform various validation checks. Some of this stuff can become "order dependent" and I therefore prefer a simple if(condition){error msg;next} structure versus a more complex if,elseif,else type of deal. This allows the order of the conditions to be moved around easily and quite frankly the computation power that can be saved in a more complex "if" structure is absolutely meaningless.
Now of course there are standard programs like getOpt and the long option version of that. I don't think that we are talking about that here.
#!/usr/bin/perl -w
use strict;
#basic "command loop"
while ( (print "Enter a command: "),
(my $line = <STDIN>) !~ /^\s*q(uit)?\s*$/i
)
{
#### Validate input and if ok, call a sub at the
#### of validation
next unless $line =~ m/\S/;
#chomp ($line); #actually optional here (\n counts as $)
if ($line =~ m/^\s*help\s*$|^\s*h\s*$/i)
{
print "there is no help for the helpless!!!\n";
next;
}
if ($line =~ m/^\s*[+-]{0,1}\s*\d+\s*$/)
{
print "wow, a number! try again!\n";
next;
}
if ($line !~ m/^add$|^del$/i)
{
print "invalid_command!!\n";
next;
}
add() if $line =~ m/add/;
del() if $line =~ m/del/;
}
sub add
{ print "some kind of add happend\n";}
sub del
{ print "some kind of del happened\n";}
__END__
Some example interaction:
C:\TEMP>perl stdcommandloop.pl
Enter a command: 34
wow, a number! try again!
Enter a command: -45
wow, a number! try again!
Enter a command: +-56
invalid_command!!
Enter a command:
Enter a command:
Enter a command: help
there is no help for the helpless!!!
Enter a command:
Enter a command: del
some kind of del happened
Enter a command: add
some kind of add happend
Enter a command: quit
> (OS prompt is back, end of program)
| [reply] [d/l] |
|
|
| [reply] |
Re: Modifying loop structure and placement
by Marshall (Canon) on Dec 24, 2009 at 21:33 UTC
|
I am just guessing again. But I figure that this some guessing game based upon some random number between 0-100. Here is one version. The rand() function is not really that random but it is good enough for this game. This shows how to start the game, validate input to the game and also how to restart a new session of the game. Certainly some refinements are possible, but this a general framework.
#!/usr/bin/perl -w
use strict;
my $secret_number;
my $total_guesses;
sub init_game
{
$secret_number = int(rand 101);
$total_guesses =0;
}
init_game(); #initial start of game parameters
print "General Rules:\n";
print " I have a secret number from 0-100\n";
print " Your job is to guess this number!\n";
print " You will be given hints for each guess\n";
print " Enter quit if you give up!\n";
while ( (print "Enter a guess: "),
(my $number = <STDIN>) !~ /^\s*q(uit)?\s*$/i
)
{
next if $number !~ /\S/; #skip blank lines
$number =~ s/^\s*//; #no leading spaces
$number =~ s/\s*$//; #no trailing spaces (also chomps)
if ($number =~ /[-.]+/)
{
print "only unsigned integers are allowed!\n";
next;
}
if ($number =~ /\D/)
{
print "Invalid!! Only integer numbers >=0 are allowed!\n";
next;
}
if ($number >100)
{
print "Hey, you didn't pay attention, max number is 100!\n";
next;
}
if (game_finished($number))
{
print "would you like to play again?:(Y|y|yes)\n";
if ( (my $answer =<STDIN>) =~ /^\s*Y(es)?\s*$/i)
{
init_game();
print "New Game Starting!!!\n\n";
}
else
{
exit(0);
}
}
}
sub game_finished
{
my $number = shift;
$total_guesses++;
if ($number == $secret_number)
{
print "Horray! you got the secret number, $secret_number!\n";
print "But it took you $total_guesses total guesses to do it!!\
+n";
return(1);
}
if ($number < $secret_number)
{
print "You guessed too low! Dummy!\n";
return(0);
}
if ($number > $secret_number)
{
print "You guessed too high! Duh!\n";
return(0);
}
}
Example Session:
General Rules:
I have a secret number from 0-100
Your job is to guess this number!
You will be given hints for each guess
Enter quit if you give up!
Enter a guess: 50
You guessed too high! Duh!
Enter a guess: 25
You guessed too high! Duh!
Enter a guess: 12
You guessed too high! Duh!
Enter a guess: 6
You guessed too low! Dummy!
Enter a guess: 8
You guessed too high! Duh!
Enter a guess: 7
Horray! you got the secret number, 7!
But it took you 6 total guesses to do it!!
would you like to play again?:(Y|y|yes)
y
New Game Starting!!!
Enter a guess: q
Update: I guess I could have combined two statements:
if ($number =~ /[-.]+/)
if ($number =~ /\D/)
into just one regex: if( $number =~/[-.\D]/)
{
print "Invalid!! Only unsigned integer numbers >=0 are allowed!
+\n";
next;
}
probably could add if( $number =~/[-.+\D]/) also to disallow the + sig
+n.
But I don't this minor issue detracts from the main point.
| [reply] [d/l] [select] |
|
|
Thanks for taking the time to create this extensive response. A lot for me to digest.
| [reply] |
|
|
A bit shorter version of the "guessing game"
#!/usr/bin/perl -w
use strict;
my $magic = int (rand 100 );
my $guesses = 1;
my $line;
print "Guess a number between 0-99\n";
while ( (print "Enter an integer: "),
$line = <STDIN>,
$line !~ /^\s*q(uit)?\s*$/i
)
{
next if $line =~ /^\s*$/; #re-pompt on blank line
if ( $line !~ /^\s*(\d{1,2})\s*$/ )
{
print "Illegal entry! Try again!\n\n";
next;
}
print "Too high!\n\n" if $1 > $magic;
print "Too low!\n\n" if $1 < $magic;
last if $1 == $magic;
$guesses++;
}
print "You got it right in $guesses guesses! \n"
if ($line !~ /^\s*q(uit)?\s*$/i);
| [reply] [d/l] |