CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: Perl Programming Help: Intermediate:
A subroutine inside another subroutine (HUGE PROBLEM)

 



actionscripter
New User

Apr 17, 2002, 11:30 AM

Post #1 of 10 (1526 views)
A subroutine inside another subroutine (HUGE PROBLEM) Can't Post

Ok, let's say I have:



use diagnostics;
#use strict;

sub test1 {
my $x=0;
sub test2 {
for (1..5) {
$x++;
print $x;
}
}
test2();
}

test1();
test1();





If the script above is run, you will notice that only the first call to the subroutine works correctly. WHY?



Also, why is the problem solved if I do the following:

1) Make $x global.
2) Make the innermost subroutine anonymous.
3) Store the innermost subroutine inside another file, and then require () that file.



Pls don't redirect me to http://perl.apache.org/guide, because I've read it but don' understand what it's talking about......


(This post was edited by actionscripter on Apr 17, 2002, 11:34 AM)


Pro_4
User

Apr 17, 2002, 1:01 PM

Post #2 of 10 (1519 views)
Re: [actionscripter] A subroutine inside another subroutine (HUGE PROBLEM) [In reply to] Can't Post

Why do you want to even create a subrountine inside of a subroutine? Seems kinda odd and that might be the problem, it should work perfectly fine if the second subroutine is outside of that first one.


actionscripter
New User

Apr 17, 2002, 7:47 PM

Post #3 of 10 (1513 views)
Re: [Pro_4] A subroutine inside another subroutine (HUGE PROBLEM) [In reply to] Can't Post

I'm asking this because of mod_perl. Mod_perl treats your whole script as one big subroutine called "handler". If you declare a subroutine inside, it will cause problems. That's why I quote the example above.

So can anyone answer my questions?


ka0osk
Novice

Apr 18, 2002, 6:01 AM

Post #4 of 10 (1504 views)
Re: [actionscripter] A subroutine inside another subroutine (HUGE PROBLEM) [In reply to] Can't Post

You can't do that. You can only call the inner rtn from INSIDE the first rtn, and THATS only if the parser is having a good day. It has to do with the way scripts are parsed. Spaghetti code is just bad programming anyway.
Even if you DO get it to work, the next version of Perl could kill your code! Consider having 2 separate subrtns, thats the right way to do it, and you know it will continue to work on new Perl versions.Crazy

John Step;


uri
Thaumaturge

Apr 18, 2002, 10:31 PM

Post #5 of 10 (1489 views)
Re: [actionscripter] A subroutine inside another subroutine (HUGE PROBLEM) [In reply to] Can't Post

first off, perl does not support nested sub declarations so do try to do it. it doesn't do what you think it does.

secondly, what you have done there is created a closure on $x. that means that test2 when it is compiled creates a private copy of $x since that variable was lexical in an outer scope. put a print statement after the my $x line and you will see it never changes. only the private copy inside test2 gets increments.

making a anon sub would fix it if it was assigned in each call to test1. that would assign the value of $x each time test1 was called and the values would start from 0. making $x a global is an obvious but poor solution.

and finally when you print diagnostic output don't smush it together as that is not conducive to debugging


dsb
stranger

Apr 20, 2002, 6:15 PM

Post #6 of 10 (1476 views)
Re: [uri] A subroutine inside another subroutine (HUGE PROBLEM) [In reply to] Can't Post

1. Perl does support nested sub routines. They help provide encapsulation to scoped variables. In other words, a variable declared with my will only be accessible through the sub routine nested in the same block, whereas the sub-routines can be called(and declared) from anywhere:


Code
  

{

my $x = 0;

sub test2 {

for (1..5) {

print ++$x;

}

print "\n";

}

}

test2(); # print '12345'

print $x, "\n"; # prints nothing, throws an error when using 'strict'



2. If you look closely at the output of the code you give, you will see that the second call to test1 IS working, but the output is '678910'. That is because test2 is accessing a private copy of $x which otherwise goes out of scope after the first call to 'test2'(via 'test1'). Initialize $x to '0' in the 'test2' sub and you will get the results you seem to want.

3. Try calling 'test2' with arguments(add the appropriate code to 'test2') and you will also get the results you seem to want. Doing this, 'test2' will be using the same private copy of $x that 'test1' is using, so when $x initializes $x to '0', 'test2' sees the re-initialization:

Code
  

sub test1 {
my $x = 0;
sub test2 {
my $x = shift;
for (1..5) {
$x++;
print $x;
}
print "\n";
}
test2($x);
}

test1();
test1();





Hope that helps.

_______________________

dsb
PerlGuy


uri
Thaumaturge

Apr 20, 2002, 6:25 PM

Post #7 of 10 (1475 views)
Re: [dsb] A subroutine inside another subroutine (HUGE PROBLEM) [In reply to] Can't Post

that is not a nested sub. that is a named sub scoped in a block which is a well known thing to do for creating the equivilent of c static variables. that is NOT what i was referring to. in perl declaring a named sub does not hide the sub name (unlike algol and PL/I). as i said, the OP accidentally created a closure there which was not his intention. there is no benefit to declaring a sub inside a sub in perl. just declaring both subs in an outer block with my $x in that block will do what he wants without being
cluttered.

and there is ongoing debate in some perl circles as to whether this is a closure


Code
{ 
my $foo ;

sub next_foo {
return ++$foo ;
}
}


some say that a closure is only created with anon subs since the copies of the closure variables need to be made and bound to the code reference. others claim the above code is also a closure since it does the same thing if only 1 time due to the named sub being used.


dsb
stranger

Apr 20, 2002, 7:37 PM

Post #8 of 10 (1473 views)
Re: [uri] A subroutine inside another subroutine (HUGE PROBLEM) [In reply to] Can't Post

I'll do some more research, that is an interesting issue uri. Thanks for the info.

One question though, if that's not a nested sub, what is? Nevermind if its not supported in Perl for now, how would code one if they were?

_______________________

dsb
PerlGuy


(This post was edited by dsb on Apr 20, 2002, 7:38 PM)


uri
Thaumaturge

Apr 20, 2002, 9:38 PM

Post #9 of 10 (1470 views)
Re: [dsb] A subroutine inside another subroutine (HUGE PROBLEM) [In reply to] Can't Post

proper nested subs share the parents data space and are also private to the parent. perl's named subs are always global and in the current package space so you can't create proper nested subs.

what you saw here looked like it and it has closure behavior but test2 can be called from anywhere in the whole program.


dsb
stranger

Apr 21, 2002, 4:36 PM

Post #10 of 10 (1466 views)
Re: [uri] A subroutine inside another subroutine (HUGE PROBLEM) [In reply to] Can't Post

Ya, that I knew(being able to call Perl subs from anywhere in program). I guess I misunderstood what a true nested sub was. Thanks for clearing that up. ;)

_______________________

dsb
PerlGuy

 
 


Search for (options) Powered by Gossamer Forum v.1.2.0

Web Applications & Managed Hosting Powered by Gossamer Threads
Visit our Mailing List Archives