newbie00 has asked for the wisdom of the Perl Monks concerning the following question:

Hello.

Goal: Within a package, to take a large subroutine and remove a chunk of code that includes 'my' declarations (which before the split are the "parent's" "my's") and put it into a separate subroutine ('child', if there is such a thing) for easier reading of the code. This chunk of code has a single purpose.

Is that possible with ease? If so, 'how'? Must more declarations be made in order for the 'parent' subroutine to see the 'child' subroutine and all of the variables (and vice versa)?

Hope this makes sense... :)

Thanks in advance.

  • Comment on Can a large subroutine be split in two (e.g. parent and child relationship)?

Replies are listed 'Best First'.
Re: Can a large subroutine be split in two (e.g. parent and child relationship)? (don't just split)
by tye (Sage) on Jul 05, 2006 at 21:03 UTC

    It usually works much better to factor out logically coherent parts into well-named subroutines that, due to being logically coherent, don't need to have a lot of information passed in and out. It is called "refactoring" and can usually make code not just easier to read but also less buggy and easier to maintain. The goals include increasing "good properties" of the code such as modularity, loose coupling, data hiding, etc.

    - tye        

Re: Can a large subroutine be split in two (e.g. parent and child relationship)?
by Zaxo (Archbishop) on Jul 05, 2006 at 21:09 UTC

    Sure it can, if it has a single purpose you can put a name to.

    To do that with ease, you can refine the big function bit by bit. Make sure that it doesn't assign to global variables, unless that's its sole purpose. Try to make the function return what it produces, getting all of its information from its arguments. That insulates the rest of the application from the functions' workings.

    Now start cutting down big lists of initial my declarations, moving each lexical variable's declaration to where it is first assigned. Then move that assignment down to just before it is first used. Find where each variable is last used, and consider that spot for the end of a lexical block. The block which most closely corresponds to the new function you're thinking of will be the body of your new function. It will tell you which outer variables correspond to arguments of the new function, and which are internal to it.

    This whole process is referred to as "refactoring", and it's worth the exercise to do it.

    After Compline,
    Zaxo

Re: Can a large subroutine be split in two (e.g. parent and child relationship)?
by GrandFather (Saint) on Jul 05, 2006 at 20:50 UTC

    Generally if you do something like that you need to pass the parent's variables into the child sub. If the parent sub needs to see changes in the variables then you need to pass references to the variables into the child sub. At that point all the references to the pass by reference variables in the child sub need to be adjusted. Consider:

    sub parent { my $valueOnly; my $needsUpdates; child ($valueOnly, \$needsUpdates); } sub child { my ($valueOnly, $needsUpdates) = @_; $$needsUpdate = $valueOnly; # was: $needsUpdate = $valueOnly; }

    Notice the second $ prepended to $needsUpdate to dereference it in the child sub.


    DWIM is Perl's answer to Gödel
Re: Can a large subroutine be split in two (e.g. parent and child relationship)
by Hue-Bond (Priest) on Jul 05, 2006 at 20:44 UTC

    If I'm understanding it right, you have something like this:

    sub foo { my ($var1, $var2, $var3, @arr1); [a large pile of code] }

    And you vant to split that code in two smaller subs but are worried about variable scoping and so. What about this?

    { my ($var1, $var2, $var3, @arr1); sub foo { [some code] bar(); } sub bar { [rest of the code] } }

    --
    David Serrano

      That creates a closure on the variables which is different behaviour than having the variables local to $foo. Consider:

      use strict; use warnings; { my ($var1, $var2, $var3, @arr1); sub foo { bar(); } sub bar { ++$var1; print "$var1\n"; } } foo (); foo ();

      Prints:

      1 2

      DWIM is Perl's answer to Gödel
        That creates a closure on the variables

        It was intended but now I realize that I didn't take into account the outcome you are showing.

        --
        David Serrano

Re: Can a large subroutine be split in two (e.g. parent and child relationship)?
by Ieronim (Friar) on Jul 05, 2006 at 21:01 UTC
    You can use a reference to a anonymous sub inside your big subroutine.
    For example, in this script the 'my' variables from the outer subroutine are visible inside $inner:
    #!/usr/bin/perl print outer('John', qw/dog car home/), "\n"; sub outer { my $name = shift; my @items = @_; my $inner = sub { return "$name\'s $_[0] "; }; return map {$inner->($_)} @items; }
    The example is quite silly, but i often use this technique to simplify code. It is useful, if $inner has no sense outside outer.
Re: Can a large subroutine be split in two (e.g. parent and child relationship)?
by jdporter (Paladin) on Jul 05, 2006 at 20:41 UTC

    It's possible to do the sort of "refactoring" that you're talking about, but it might not be as simple as you're hoping; it really depends on what variables need to be seen by both routines and how they'll be used. If you show the routine you're trying to split, or one similar to it in essential respects, we can take a hack at it.

    We're building the house of the future together.
Re: Can a large subroutine be split in two (e.g. parent and child relationship)?
by betterworld (Curate) on Jul 05, 2006 at 22:03 UTC
    Alternatively, you could get an editor which supports folding. In vim you could do it like this:
    sub foo { # {{{ variables my ($var1, $var2); # }}} # {{{ actions bar($var1, $var2) until $var1; # }}} } # vim: set foldmethod=marker:
Re: Can a large subroutine be split in two (e.g. parent and child relationship)?
by derby (Abbot) on Jul 05, 2006 at 22:07 UTC

    In a word - yes (depending on your definition of 'with ease').

    -derby
      Thanks all. You've given me something to work with and consider.