Re: Resetting variables
by davido (Cardinal) on Dec 21, 2004 at 17:25 UTC
|
Lexical scoping exists to make your life easier. You don't need to reset variables if you use lexical scoping to your advantage. Here's a very concise example:
use strict;
use warnings;
my $i = 0;
while( $i++ < 10 ) {
no warnings qw/uninitialized/;
my $scoped;
print "\$scoped contains $scoped\n";
$scoped = 100;
}
This snippet uses strict and warnings, but for the purposes of this particular demonstration turns off the "uninitialized value" warning. Next, it loops ten times. Each time, it creates a lexically scoped (my) variable called $scope. It prints that variable's contents (which are essentially empty, or undefined). It then assigns the number 100 to $scoped, and loops. If you weren't using lexical scoping, on the second loop iteration, $scoped would still contain 100, and that would print. But as you see if you run this, on each iteration $scoped is still empty. Why? Because at the end of each while block iteration, the lexical variable named $scoped "falls out of scope" (it disappears, forever gone). And on the next iteration, a new one is created (via 'my').
What you need to do is write your script in such a way that when the player decides to play again, all the variables that need to be reset simply fall out of scope. The ones whos values should be kept would need to have been defined at a broader scope.
This sort of thing is described in perlintro, perlsub and perlsyn, for starters. Therein you'll also read about properly passing parameters to subroutines rather than absorbing values through global osmosis. Welcome to a new way of thinking.
| [reply] [d/l] |
|
|
| [reply] [d/l] |
|
|
The particular bug that's causing the error message you're referring to is in the reset_game() sub.
The problem is that you're assigning a single scalar value '0' to a list. That means that $all_occurrences becomes undefined. Use an undefined value within a character class in a regexp and you get an error message. Try the followng:
perl -e "my $var; 'string'=~/[$var]/"
And here's the error...
Unmatched [ in regex; marked by <-- HERE in m/[ <-- HERE ]/ at -e line
+ 1.
If you're getting warnings when running under warnings and strictures, the goal is to understand why. The warnings are telling you something. In this case, you mentioned getting uninitialized value warnings. Instead of fixing the problem, you subverted the warnings, and that allowed bugs to creep in. If you had dug into why you were getting those undefined value warnings, you would have tracked the problem down to your reset sub.
| [reply] [d/l] [select] |
|
|
|
|
|
Re: Resetting variables
by BrowserUk (Patriarch) on Dec 21, 2004 at 17:23 UTC
|
It is very hard to see how a hard coded regex would suddenly become invalid on a second pass. Indeed, it can't. At least not without something very, very unusual happening.
I think you need to post the complete script that demostrates the problem, because your description of the problem does not make great deal of sense, and without being able to run the same code you are running and thereby reproduce the same errors you are encountering, there is no real way to help you.
"But you should never overestimate the ingenuity of the sceptics to come up with a counter-argument." -Myles Allen
"Think for yourself!" - Abigail
"Time is a poor substitute for thought"--theorbtwo
"Efficiency is intelligent laziness." -David Dunham
"Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon
| [reply] |
Re: Resetting variables
by trammell (Priest) on Dec 21, 2004 at 17:26 UTC
|
A couple of suggestions:
- use strict; and use warnings;
- Use globals only where necessary, to prevent scary action-at-a-distance and simplify debugging.
| [reply] |
Re: Resetting variables
by Roy Johnson (Monsignor) on Dec 21, 2004 at 17:30 UTC
|
The code you included is not enough to duplicate your problem. In fact, it doesn't run because the print_word sub isn't defined. After defining it, I got a lot of complaints about the global variables, because I included
use strict;
use warnings;
at the top of the program -- you should do that. Then I declared and initialized where necessary ($word_len and $tot_found need initial values), and ran the program without a problem (although it doesn't do anything interesting).
The error you describe strikes me as very quirky. You might get around it by tacking the "o" (compile once) option onto the regex:
/[[:alpha:]]/o
Caution: Contents may have been coded under pressure.
| [reply] [d/l] [select] |
Re: Resetting variables
by Random_Walk (Prior) on Dec 21, 2004 at 17:50 UTC
|
Sadly you have not posted enough of your code to compile. Perhaps you can try to reduce the problem to the minimum set that still runs and exhibits the problem.
If you scope each session of the game and its variables so that when one cycle is complete they go out of scope you will not have to call a reset_game routine
use warnings;
use strict;
# variable we want to keep between runs
my $high_score=0;
my $play_it_again=1;
while ($play_it_again) {
my $last_score = play_game;
$high_score = $last_score if $last_score > $high_score;
$play_it_again=ask_user;
}
sub play_game {
my $game_score=0;
# game logic
return $game_score;
}
sub ask_user {
print "Do you want to play again (y or n)? ";
my $play_again = <STDIN>;
chomp($play_again);
$play_again eq "y" ? 1 : 0
}
use strict; Don't know if you do as this is obviously a small frag of something bigger and you do appear to use my aplenty
Cheers, R. | [reply] [d/l] [select] |
Re: Resetting variables
by Sandy (Curate) on Dec 21, 2004 at 21:28 UTC
|
By jove, I think I got it...
Firstly, although I am not certain, I believe the error message is erroneously pointing to the wrong line
$guess !~ m/[[:alpha:]]/
rather the error occurs in the elsif portion, where you have
$guess =~ /[$good_guesses]/
During the 1st pass, $good_guesses is either undefined, or set to a single letter (or multiple letter? doesn't matter).
On second pass, the $good_guesses is reset to a null string.
$guess =~ /[$good_guesses]/
gets translates to ...
$guess =~ /[]/
However, if I am not mistaken, character classes will accept a ']' as a valid element of the character class, if it is the first element of the class. So, the parser is taking the ']' as an element of the class, and then continues to look for the closing square bracket.
For whatever reason, if $good_guesses is undefined, the parser figures it out the way you meant. (UPDATE: Nope, that's not the reason, see update below)
To get your code to work, I re-initialized $good_guesses = undef; in your "reset_game" sub.
PS: I wrote my response after I got your program to work, but I did not actually go and read up on all the particulars of character classes in regular expressions, so I may not be entirely accurate.
Sandy
PS (again) ... That was fun!
UPDATE: Made mistake... the reason the code works when $good_guesses is undefined is that there is a condition in front of the condition...
elsif ((defined($good_guesses))&&($guess =~ /[$good_guesses]/))
that prevents the interpretation
$guess =~ /[]/
when $good_guesses is not defined | [reply] [d/l] [select] |
|
|
Hmm, I believe I am checking to make sure $good_guesses is defined (unless I don't understand what defined() really does):
} elsif ((defined($good_guesses))&&($guess =~ /[$good_guesses]/)) {
print "\n***You already guessed that letter!*** (hit any key)\
+n"; <>;
} else {
if ($word =~ /[$guess]/) {
if (defined($good_guesses)) {
$good_guesses .= "$guess" unless ($guess =~ /[$good_gu
+esses]/);
} else {
$good_guesses .= "$guess";
}
| [reply] [d/l] |
|
|
my $var; # variable is undefined
if ($var) { ... } # $var is false because it is undefined
if (defined $var) {...} # false
$var = "";
if ($var) { ... } # false because $var is null string
if (defined $var) {...} # true because $var has been
# defined as having a null string
$var = 0;
if ($var) { ... } # false because $var is zero
if (defined $var) {...} # true because $var has been
# defined as zero
| [reply] [d/l] |
|
|
Yes, you appear to be right Sandy! I comment out two chunks that are doing stuff with $good_guesses, and it works (well, not the way I want, but I don't get the error). Now I have to figure out how to fix that, because it is now doing it on every run, not just the second pass. I don't know why though, because I'm defining $good_guesses = "" at the top.
A lot of folks have wagged fingers at me for using global variables, and I know what they mean. I just don't know how else to do it! If I want to have a variable that needs to be accessed/updated by the main loop and by multiple subs...how can you do that without storing the variable outside of all the subs? If I try to put it in the loop it will be zeroed out on every loop, and the same with the subs. Any ideas would be appreciated.
This is what I mean:
| [reply] [d/l] |
|
|
Oops, our posts have crossed in the mail (so to speak).
Try either
if ($goodguesses && ($whatever =~ /[$goodguesses]/) {...}
or undefine $goodguesses
$goodguesses = undef;
see previous post (just before your last one, for the differences between 'false' and 'defined' | [reply] [d/l] [select] |
|
|
Re: Resetting variables
by yacoubean (Scribe) on Dec 21, 2004 at 19:02 UTC
|
This is still giving me problems. I've made a couple of modifications following the above suggestions, and now I'm getting the before mentioned error on the first run.
if ($guess !~ /[[:alpha:]]/) {
print "\n***Invalid letter*** (hit any key)\n"; <>;
}
The weird thing is that if I guess a letter, I get the error. If I guess a number (the condition matches) I don't get an error. What gives? It should just skip the statement when the condition doesn't match...
Here's all my updated code:
| [reply] [d/l] [select] |