http://qs321.pair.com?node_id=108764

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

I'm trying to improve my programming style and have read perlstyle and will probably pick up the book Code Complete. I have, however, a question about organizing subroutines. What if I have a program that has all of it's logical functions broken up into subroutines and they're not passing each other data. This would work:
func1(); func2(); func3(); sub func1 { } sub func2{ } sub func3{ }
Or would this be better:
func1(); sub func1{ func2(); } sub func2{ func3(); }
I'm just not sure what's the best programming practice even if several options will work. Certainly not one big function with all global variables. TIA...Kevin

Replies are listed 'Best First'.
Re: Style & subroutine organization
by derby (Abbot) on Aug 29, 2001 at 19:27 UTC
    Camelman,

    Good job on Code Complete. The following is somewhat extracted from there and other sources.

    The two criteria for function "goodness" are coupling and cohesion. Cohesion can be thought of as the "internal" make-up of the function - does it do one (or a few small things) well. Coupling is how related one function is to another. The goal is to design/implement functions (or procedures, or methods, or whatever) that have high cohesion and low coupling.

    With your second example

    func1(); sub func1{ func2(); }
    We have mediocre cohesion and high coupling. Mediocre cohesion because not only does func1 do "its thing" but also because it has a secondary function of controlling flow. There is a high coupling between func1 and func2 because you need to go through func1 to get to func2. This is not always a bad thing but I think we can avoid it here.

    That all being said, there are some functions whose sole purpose is to control the flow. Sometimes they're called dispatch functions and other times traffic controllers. (Which is what suaveant explained)

    -derby

      Something like this was discussed here already...
      Everything was discussed here, at least once, right...;-)

      Check i.e.: (Software Engr) Encapsulate! No,no,no! Decouple!.

      pmas
      To make errors is human. But to make million errors per second, you need a computer.

      I appreciate everyone's help here. Next time I'll write an example that is more concrete. My attempt at brevity backfired. ;-) Probably what I'm getting at is the use of "dispatch functions." I'm really tring to incorporate local variables, use strict, and passing references between arrays so I'm coming up with some odd questions. I'm a UNIX admin with no formal Computer Science training so I rely on O'Reilly and perlmonks for my information. Thanks again . . . Kevin
Re: Style & subroutine organization
by suaveant (Parson) on Aug 29, 2001 at 18:45 UTC
    Of course, if you were going to run all three in a row a lot you could always do...
    sub all_funcs { func1(); func2(); func3(); }
    and call that when you need them, that way you can call them all together or seperately easily.

                    - Ant
                    - Some of my best work - Fish Dinner

Re: Style & subroutine organization
by jryan (Vicar) on Aug 29, 2001 at 18:38 UTC
    Definately your first example. The point of a function is to have a reusable code block. Therfore, its best to have each function just do the specific task that you want, even if in this example they will be called consecutively. You may find later on that you might need to use that particular function in a different program, and it will be much easier to implement if the function doesn't have a lot of clutter that needs to be removed.
Re: Style & subroutine organization
by idnopheq (Chaplain) on Aug 29, 2001 at 18:38 UTC
    Unless I'm reading it wrong, options one and two are not equivallent. The first calls 1 then 2 then 3 independent of each other. The second calls 1 which calls 2 which calls 3, 1 dependent on 2 dependent on 3 ( which is missing ).

    Part of why subs exist is to ease repetitive tasks w/i code. One applies appropriate style based on need. There will be some instances where option one is best, and others where option two rules.

    If you can post a specific example, maybe we can provide a better answer to your somewhat vague question.

    HTH
    --
    idnopheq
    Apply yourself to new problems without preparation, develop confidence in your ability to to meet situations as they arrise.

      Sorry about the vague question. All three of these functions would be independent in the sense that they do not pass each other data but I would like to execute them in a row. I meant the execution order in my two options to be equivalent. As silly as it seems, something like this:
      #!/usr/bin/perl -w use strict; func1(); func2(); func3(); sub func1{ print "I am the first subroutine\n"; return 0; } sub func2{ print "I am the second subroutine\n"; return 0; } sub func3{ print "I am the thrid subroutine\n"; return 0; }
        The way listed here is the canonical way of calling three completely-independent functions in a row. It is very readable and maintainable.

        Maintainable, just to go off on a slight rant, is where there are no hidden assumptions. If you were to do:

        sub foo1 { foo2(); } sub foo2 { foo3(); } sub foo3{ }
        I would assume that foo1() depends on the result of foo2() in some fashion. If it doesn't, then I am very confused. Your piece of hidden information here is that there is no dependence between the function, other than they all need to be executed at the beginning of your script.

        If you reduce the amount of information that someone needs to know that cannot be directly deduced from reading the logical structure of your code (you do indent logically, right?), that improves the maintainability of your code. Maintainable code should be your holy grail, above and beyond any other consideration (except for correctness). It should be more important than optimization (save for when business needs demand it).

        ------
        We are the carpenters and bricklayers of the Information Age.

        Vote paco for President!

Re: Style & subroutine organization
by reyjrar (Hermit) on Aug 29, 2001 at 19:04 UTC
    I'm not clear on what you want to do, but if you want to pass the return value of func1() to func2() and the return value of func2() to func3() then you just have to:
    func3( func2( func1() ) );

    if you're looking to abuse some perl internals, then you can using the special variable @_ and function passing using &subroutine.
    When you call a function like this: &foo; foo is passed the current value of @_. Also, did you know that manipulating @_ inside a function actually manipulates the passed variables? Kinda like pass by reference in c. check this out:
    use subs qw/foo bar baz/; @_ = ( 1, 2, 3, 4 ); &foo; # sends current contents of @_ print join(' ', @_), "\n"; &bar; # sends current contents of @_ print join(' ', @_), "\n"; &baz; # sends current contents of @_ print join(' ', @_), "\n"; &baz(); # sends an empty list print join(' ', @_), "\n"; sub foo { map { $_++; } @_; # map actually changes the values in @_ } sub bar { map { $_+=2 } @_; # again, map changes @_ } sub baz { map { $_--; } @_; # also chainging @_; }

    I don't know which you're trying to accomplish, but becareful using these tricks as it could have unwanted side effects. Also note that calling a sub routine like this: &foo() over rides the default behaviour of passing @_ to the function and sends it an empty list ().

    hope that helps,
    -brad..
      I usually prefer to keep the scope levels in mind. Because perl doesnt have a defined main block and some of the subtler uses of scope (anonymous blocks for instance) and BEGIN{} END{} blocks I try to keep things organised so its intuitive. For instance
      #!perl # GLOBAL DEFINITIONS # TYPEGLOBMANIPULATIONS # BEGIN{} # FUNCTIONS # ANON_BLOCKS # END{} # main() equivelent. __DATA__
      Because this kind of thing can get confusing:
      my $var; sub foo {$var++; #ie code} my @list; use Some::Class; sub bar{} my %hash; sub sna{} sub fu{} my $scalar; # code # .... __DATA__
      Now you might say "what if a lexical variable is used only by one particular funct ie static lexical" well then I usually do something like this
      { #Anonymous block for the following static lexicals: my $static_scalar; my @static_array; sub push_stack { push @static_array,@_} sub pop_stack { pop @stack} } #End anonymous block
      So the first would probably end up like this:
      use Some::Class; sub sna{} sub fu{} { my $var; sub foo {$var++; #ie code} } { my @list; sub bar{} } my $scalar; my %hash; # code # .... __DATA__
      Much easier to figure out whats gone 12 months later i assure you. Yves
Re: Style & subroutine organization
by Maclir (Curate) on Aug 29, 2001 at 21:39 UTC
    I would have to support the first option. As a rule of thumb, it is generally considered "good practice" to have all of the declarations for a block of code at the top, along with a concise description of any declarations that may add to readibility.

    One other thing to consider is the size (in lines of code) of each subroutine. In my early days as a Cobol programmer, the site standards specified that a subroutine should not be longer than two pages of standard 66 line fanfold printout. That way, you could see the complete subroutine at a single opening of the printout binder. Now, we have long since moved beyond filing out program listings in binders of 132 x 66 sized listings, but the idea still has validity - you should be able to take in the who subroutine at a single reading. Hence, make each routine do a single task - no side effects.

Re: Style & subroutine organization
by perrin (Chancellor) on Aug 29, 2001 at 22:14 UTC
Re: Style & subroutine organization
by Anonymous Monk on Aug 29, 2001 at 22:32 UTC
    It drives me nuts when guys seperate their programs into multiple subroutines for no good reason, and I get especially excercised when you interleave the subroutine definitions with the main code which calls them.
    If you _really_ want to make me crazy, nest each function within the previous one to add needless complexity! I can't believe they taught you to do that in school.
    If these subroutines are doing significantly different or reusable things, maybe. Otherwise just make it all one long block.
    I'm NOT encouraging you to make all the variables global. you can certainly scope your variables within the blocks of code in which they're used. Heck, enclose the blocks in braces {} to limit the scope of a variable, but they DON'T have to be functions!
    Take this:

    func1();
    func2();
    func3();

    'twas indented, but the silly formatter mashed that....

    sub func1
    {
    my $variables;
    }
    sub func2
    {
    my $more_variables;
    }
    sub func3
    {
    my $other_variables;
    }

    and eliminate everything but the braces:

    {
    my $variables;
    # Code to use these variables
    } # End of scope of $variables
    {
    my $more_variables:
    # Code uses more_variables
    } # End of scope of $more_variables
    {
    my $other_variables
    # Use other_variables
    } # end of scope of $other_variables.


    I usually find myself making enough blocks in braces as a "natural consequence" of conditional statements and other flow control to not have to add additional braces to control the scope of my variables.
      Item. When a single block goes over a page on your editor, your error rate jumps.

      Item. When you write your program as a series of functions, it is easy to incrementally test your work as you develop it. Incremental testing helps overall code quality and improves overall development speed.

      Item. If you develop your program as a series of functions, if halfway through development you find that you had a logic error, it is generally easier to adjust the overall program flow.

      Item. When you make a habit of trying to make your code be more modular and reusable from day one, you start finding a lot more opportunities to reuse code. Reuse is one of those things that you won't find unless you always keep your eyes out for it.

      Item. When you call a series of well-named functions, the result is code which clearly documents how it is supposed to work, but without the synchronization issues that scattering excessive comments can lead to. (Note that this is but one documentation need.)

      There are more reasons listed at Re (tilly) 1: When do you function?.

      Note that this is not a style that I learned, "In school." I studied math in school. Rather this is the style that is common to the best programmers I have run across in the workplace, on CPAN, and in books on good programming organization. Also note that the books on good programming technique that I am talking about are books like Code Complete and The Pragmatic Programmer. These are not books written by academics. Rather they are books written by experienced programmers with years in industry. Admittedly programmers who are willing to pay attention to academic research. But ultimately people whose opinions on programming are strongly influenced by decades of writing code.

Re (tilly) 1: Style & subroutine organization
by tilly (Archbishop) on Aug 30, 2001 at 00:32 UTC
    If you are not passing your functions data, where are they getting data from, global variables?

    If so, then the first important improvement that I would suggest to your style is to use strict.pm and massively reduce the number of globals you use...

Re: Style & subroutine organization
by HamNRye (Monk) on Aug 30, 2001 at 00:07 UTC

    I do not maintain that this is the best way to do it, it's just my way....

    I like to think of my subroutines in terms of giving instructions.

    @lines = &readFile ; @output = &parseLines(@lines) ; &doReport(@output) ;

    The reason I use this method is for readability. Read the file to get the lines, parse the lines to get the output, use the output to do the report. It allows me to quickly find the errant code block if there is a problem, and in the design phase I am left with a good outline to build my program around.

    Now, on to questions of efficiency, mem footprint, etc... I don't really care. According to my boss, all of the servers work fast enough and I don't. Hence, my main goal is to speed up my work. Also, in a modern computer you should have better than 64MB of RAM, and the space taken up by some of the extra arrays is totally negligible.

    Also, look at this write up by Merlyn on references. Passing references to subs will simplify your life at some point.

    To redo your example my way:

    $f1 = func1(); $f2 = func2($f1); $f3 = func3($f2); print $f3; (or) print func3($f2);
    Hope it helps... ~Hammy