dcsimg

The Perl You Need to Know, Part II: Working with Nested Subroutines Page 5

By Stas Bekman (Send Email)
Posted Sep 1, 2000


Now you understand that there are cases where your normal subroutines can become inner, since if your script was a simple:

  simple.pl
  ---------
  #!/usr/bin/perl 
  sub hello { print "Hello" }
  hello();

Wrapped into a run() subroutine it becomes:

  simple.pl
  ---------
  package cache::simple_2epl;
  
  sub run{
    #!/usr/bin/perl 
    sub hello { print "Hello" }
    hello();
  }

Therefore, hello() is an inner subroutine and if you have used my() scoped variables defined and altered outside and used inside hello(), it won't work as you expect starting from the second call, as was explained in the previous section.

Remedies for Inner Subroutines

First of all, there is nothing to worry about, as long as you don't forget to turn the warnings On. If you do happen to have the "my() Scoped Variable in Nested Subroutines" problem, Perl will always alert you.

Given that you have a script that has this problem, what are the ways to solve it? There are many of them and we will discuss some of them here.

We will use the following code to show the different solutions.

  multirun.pl
  -----------
  #!/usr/bin/perl -w
  
  use strict;
  
  for (1..3){
    print "run: [time sh]\n";
    run();
  }
  
  sub run{
  
    my  = 0;
  
    increment_counter();
    increment_counter();
  
    sub increment_counter{
      ++;
      print "Counter is equal to  !\n";
    }
  
  } # end of sub run

This code executes the run() subroutine three times, which in turn initializes the variable to 0, every time it is executed and then calls the inner subroutine increment_counter() twice. Sub increment_counter() prints 's value after incrementing it. One might expect to see the following output:

  run: [time 1]
  Counter is equal to 1 !
  Counter is equal to 2 !
  run: [time 2]
  Counter is equal to 1 !
  Counter is equal to 2 !
  run: [time 3]
  Counter is equal to 1 !
  Counter is equal to 2 !

But as we have already learned from the previous sections, this is not what we are going to see. Indeed, when we run the script we see:


  % ./multirun.pl

  Variable "" will not stay shared at ./nested.pl line 18.
  run: [time 1]
  Counter is equal to 1 !
  Counter is equal to 2 !
  run: [time 2]
  Counter is equal to 3 !
  Counter is equal to 4 !
  run: [time 3]
  Counter is equal to 5 !
  Counter is equal to 6 !

Obviously, the variable is not reinitialized on each execution of run(). It retains its value from the previous execution, and sub increment_counter() increments that.

One of the workarounds is to use globally declared variables, with the vars pragma.

  multirun1.pl
  -----------
  #!/usr/bin/perl -w
  
  use strict;
  use vars qw();
  
  for (1..3){
    print "run: [time sh]\n";
    run();
  }
  
  sub run {
  
     = 0;
  
    increment_counter();
    increment_counter();
  
    sub increment_counter{
      ++;
      print "Counter is equal to  !\n";
    }
  
  } # end of sub run

If you run this and the other solutions offered below, the expected output will be generated:

  % ./multirun1.pl
  
  run: [time 1]
  Counter is equal to 1 !
  Counter is equal to 2 !
  run: [time 2]
  Counter is equal to 1 !
  Counter is equal to 2 !
  run: [time 3]
  Counter is equal to 1 !
  Counter is equal to 2 !



Comment and Contribute

Your name/nickname

Your email

(Maximum characters: 1200). You have characters left.