Phoenix.pm: Tried Perl debugger on this code (Was Please help, my brain is fried)
Shay Harding
mekla at geocities.com
Sun Jan 30 10:08:10 CST 2000
That's what I thought, but wanted to verify. Thanks.
Shay
>
> > I like the above code, but I think my whole question was missed...
>
> [...]
>
> > sub get_char(){
> > srand;
> > $count++;
> >
> > my $char = int(rand(57))+65;
> > print $count, " ", chr($char), "\n";
> >
> > get_char() if ($char >= 91 && $char <= 96);
> > return $char;
> > }
>
> There were a number of questions in the section I snipped. I'll attempt
> to answer what I think are the relevant ones. (I'll skip the ones
> regarding the perl debugger since I rarely use it. It confuses me
> too.)
>
> Before I get into a discussion of why the above code doesn't work and
> the reasons why, I hope you'll permit me to simplify it somewhat by
> removing code which I suspect was added for debugging purposes. My
> rewrite (with line numbers added to make discussion easier) looks like
> this:
>
> 1 sub get_char {
> 2 my $char = int(rand(57))+65;
> 3
> 4 get_char() if ($char >= 91 && $char <= 96);
> 5 return $char;
> 6 }
>
> The intent of the above code is to return the ASCII code for a
> randomly chosen alphabetic character, i.e, one of 'A'..'Z','a'..'z'.
>
> Let us begin our discussion with line 2. We would like line 2 to
> set $char to an integer between 65 ('A') and 122 ('z') inclusive.
> As written, this line will fail to ever set $char to 122. It
> should be changed to read as follows:
>
> 2a my $char = int(rand(58))+65;
>
> In order to verify that this is correct, remember that int(rand(58))
> will evaluate to an integer between 0 and 57 inclusive. This means
> that the lowest value achievable by $char will be 65 and the greatest
> is 65+57=122, which is precisely what we want.
>
> Now let's turn our attention to the 'my' declaration. The 'my'
> declaration declares a statically scoped local variable. The
> scope extends from the point of the declaration to the ending
> right curly brace on line 6. This means that when get_char
> is invoked, $char is only visible between lines 2 and 6 for that
> invocation. (Go back and read "for that invocation" again; it's
> important.
>
> Let's consider an example. Suppose you enter get_char and were
> unfortunate enough to have $char set to 91 ('['). Once you get down
> to line 4, perl will invoke get_char again because it happens to fall
> between 91 and 96 inclusive.
>
> Now the next bit is *very* important. When you enter get_char again,
> you get a brand spanking new $char that is only visible between
> lines 2 and 6 (for this invocation). The $char in the calling
> invocation is *not* visible to the current one. Let's suppose in
> this invocation that you get $char set to 65 ('A'). That means that
> line 4 will *NOT* cause get_char to be invoked yet again. Line 5
> will cause 65 to be returned.
>
> So now we're back in the original invocation of get_char. What
> happened to our 65 returned by the recursive call? It is discarded
> because the code, as written, doesn't do anything with it. $char
> in this invocation is still set to 91 and that is what is returned.
>
> Let's consider what happens with one attempt to "fix" this code:
>
> 4a get_char() while ($char >= 91 && $char <= 96);
>
> In the above line (4a), the 'if' has been replaced by a 'while'. This
> is quite possibly worse than the original version because (as you
> observed), it is prone to hanging. Reread my above example. The only
> difference here is that when get_char returns, the while statement
> will check again to see if $char has changed so that it is no longer
> in the indicated range. Well, it *can't* have changed. There's no
> way it could have changed since the recursive invocation of get_char
> has no way to affect the instance of $char in the frame under
> consideration. Thus, line 4a will continue to call get_char() over
> and over again in the vain hope that $char will somehow get changed.
>
> Finally, let's consider a correct fix:
>
> 4b $char = get_char() if ($char >= 91 && $char <= 96);
>
> This version does two things that the original version did not.
>
> First, it actually does something with the return value of get_char.
> If you have a subroutine which returns a value and you also have
> instances of calls to that subroutine that do nothing with the return
> value, that should be a red flag that something is likely wrong.
>
> Second, and more importantly, line 4b sets $char to the return value
> of the recursive call to get_char. This means that $char in the
> outermost invocation of get_char will be set to a non out-of-range
> character.
>
> Here is the final corrected version of get_char:
>
> 1 sub get_char {
> 2a my $char = int(rand(58))+65;
> 3
> 4b $char = get_char() if ($char >= 91 && $char <= 96);
> 5 return $char;
> 6 }
>
> You might wonder if this version is susceptible to infinite recursion.
> It is not, so long as the pseudo-random number generator doesn't get
> stuck in a cycle of generating only out-of-range characters. Eventually,
> there will be a recursive invocation which will pick an in-range
> character. When this happens, the in-range character will propogate
> back to the outermost invocation due to the assignment statement
> added to 4b.
>
> I suspect that much of your confusion stems from the difference between
> 'my' and 'local'. I took a look at the perlfaq7 man page in hopes
> that it would contain a cogent discussion of the issues that I could
> refer you to, but after reading it I concluded that you may well have
> been misled by the cursory treatment that it gives to the matter. (It
> says nothing about what happens when you have recursive subroutines.
> In fact, it does give the impression that there's only one copy which
> is certainly not the case.)
>
> So, instead, I'll direct your attention to the perlsub man page. See
> the section called "Private Variables via my()". In particular, the
> following section is especially relevant...
>
> Unlike dynamic variables created by the "local" operator,
> lexical variables declared with "my" are totally hidden
> from the outside world, including any called subroutines
> (even if it's the same subroutine called from itself or
> elsewhere--every call gets its own copy).
>
> Note that last parenthesized bit, "every call gets its own copy".
>
> Kevin
>
> --
> Kevin Buettner
> kev at primenet.com, kevinb at redhat.com
More information about the Phoenix-pm
mailing list