Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

A question about the best way to use subroutines

by scottstef (Curate)
on Mar 25, 2001 at 07:30 UTC ( [id://66946]=perlquestion: print w/replies, xml ) Need Help??

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

I didn't even know where to look for this one. I am trying to learn good programming habits as well as learning pearl. For example:
if i was writing a progam to test for a file, if it was there to append it, if it wasn't create it. If i had to do it say 100 times for different files,what is the best way to use subroutines?
Do I create a lot of small 3-4 line very specific ones and then repeatedly call them numerous times?ie sub OpenFileOfFilenames, sub CheckToSeeIfFileExists, sub AppendFile/CreateFile.
Do I create a single large function that does all of this in one fell swoop?
Do I write a function that just calls all of the small functions and gets passed the list of arguments?

What is the preferred method of writing code? How do most people do it? Create a small very specific function, and then reuse it or create a larger more powerful function. Personally I prefer a lot of tiny specific functions.

Replies are listed 'Best First'.
(Ovid) Re: A question about the best way to use subroutines
by Ovid (Cardinal) on Mar 25, 2001 at 15:43 UTC
    I love questions like this. So many people focus on the "how do I do X" instead of the "why do I do X". Trying to figure out the "why" has been a subject of interest to me lately and I enjoy reading the responses when these issues pop up.

    A couple of simple answers (with liberal amounts of self-promotion): in this node, I was comparing and contrasting two different approaches to the same programming problem. The one that I listed on the top was how I prefer to code. The example I listed underneath was how the code often comes out. If you read through them carefully, you'll see that the difference stems, primarily, from how subroutines are used.

    Generally, every subroutine should do pretty much one thing and do it well. If you have a subroutine that validates someone's username/password combination, it shouldn't also be going out and grabbing the lastest CNN headlines. Some routines, though, need to do other things. I treat them as "traffic police." I have them do as little as possible except figure out where the rest of the program is going. As a result, everything pretty much gets broken down into a series of successively smaller functions with appropriate names. Here's the main snippet from one of my programs:

    #!C:/perl/bin/perl.exe -w use strict; use URI::Escape; use HTML::Entities; use CGI::Pretty qw/:no_debug/; $CGI::Pretty::INDENT = "\t"; my $q = CGI->new; my $table_data = create_table_data(); my $source_code = read_source_code(); create_web_page( headers => [ 'Symbol', 'ASCII Value', 'URL encoded', +'HTML code' ], data => $table_data, code => $source_code ); exit;
    Note: You can find the entire program at the bottom of this page, if you're really curious.

    If you wanted, you could go down into each of the subs and figure out exactly what they do, but from just these few lines, you already have a decent idea of what the program is doing. Further, since the subroutine names are descriptive, this allows me to exclude a lot of extraneous documentation.

    I think the "documentation" point is underappreciated. I'm sure you've read before about the perils of overdocumenting:

    # add $y to $x $x += $y; # If $x is greater than three, give them some output, # otherwise, we die if ( $x > 3 ) { give_them_some_output( $x ); } else { die "This is a stupid program."; }
    I think virtually everyone appreciates the stupidity of the above comments. If comments are needed there, they should be telling why we die if $x <= 3. However, if we have broken our program down into a successively smaller series of subroutines, and if those subroutines are appropriately named, once again the documentation issue becomes less significant:
    &initialize_global_variables; create_debugging_log( $debug_file_location ); grant_editor_priveleges( $user ) if $user eq "Admin";
    You may not know what any of the above subs do, but you probably have a pretty good idea. Further, if you know what the output of the program is like, having things broken down like that is a gift from the powers that be.

    Cheers,
    Ovid

    Update: Another three points I should have made:

    1. Code that is broken down into small, modular chunks (via subroutines) is much easier to maintain as you will have fewer side-effects with each section. Do you know its inputs and outputs? That's all you really need to know fix that section of code. By not breaking things down to that level, it's much tougher to determine the scope of your changes.
    2. Back to documention: documentation can be problematic because you have to maintain two things: the code and the docs. Very often, a programmer will update the code but "forget" the documentation. Because code broken down to a bunch of sub calls tends to be more self-documenting, the actual documentation can be reduced to "why" the program is doing stuff as opposed to "how". Since the "why" changes less frequently, there is less chance that the documentation gets out of sync.
    3. Last point: have you ever noticed how long-winded that Ovid guy gets when he has time on his hands?

    Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

Re (tilly) 1: A question about the best way to use subroutines
by tilly (Archbishop) on Mar 25, 2001 at 18:14 UTC
    This question also came up at When do you function?. As I said in that discussion, I like having lots of simple functions. But more important than having simple functions is having simple APIs.

    So build on them. If you have a number of well-designed simple functions sitting around, then you should have little difficulty in producing another simple function that calls your other ones in order. Now the person who uses your code can call the one official function with a simple API, but anyone can read that and break it down...

    Now one warning. Before going off and building your NiftyAPI™ it is worth putting a little energy into reading Perl's documentation. You see Perl comes with a wide range of functions that are fairly well suited to commonly felt needs. So in your case if you read open you will find that:

    local *FH; open(FH, ">> $file") or die "Cannot append to $file: $!";
    already will create the file if it needs to. Now what it doesn't do is the error check. It also won't create the path to the file. But if all you need is the file, then Perl already does the work for you. Likewise it is better to learn the various file test operators than write verbose functions for that.

    However I have in the past happily created custom operations whose value was that they would create files relative to named configuration variables, creating paths if needed. Now would I expect to create the path every time? No. But using this I created a code-base for which a test environment was easy to create. Just change the configuration variables and run the program. It would create anything and everything that it needed to see...

Re: A question about the best way to use subroutines
by indapa (Monk) on Mar 25, 2001 at 08:35 UTC
    One of my CS instructors in college said if a sub-routine was bigger than the palm of your hand then the subroutine was too big. I guess alot of it up to the discretion of the programmer. I had a look at a Perl program where there was alot of repetitive code, that if it had been put in its own subroutine, would have made the program alot shorter, easier to debug, and maintain.(And understand!) Just putting in my two cents...
Re: A question about the best way to use subroutines
by MeowChow (Vicar) on Mar 25, 2001 at 12:30 UTC
    Here's some fairly common heuristics for you. Factor code out into subroutines when:
    • You see sections of code that look nearly identical.
    • You realize that some specific task your code performs is just a particular case of a more general task.
    • You can imagine a scenario in which your code would be copied-and-pasted somewhere else.
    • You can think of your code as performing a "black-box" transaction.
       MeowChow                                   
                   s aamecha.s a..a\u$&owag.print
Re: A question about the best way to use subroutines
by tune (Curate) on Mar 25, 2001 at 10:53 UTC
    Sometimes it is good just to work out the steps of your program in different subroutines. It makes it very understandable and clear.
    E.g. i am sleepy now, so
    #!/usr/bin/perl &stand_up(); &brush_teeth(); &go_to_bed(); &fall_asleep(); exit (0); # i don't go into details about the subs now :-)
    Sometimes it is very useful to store your reusable routines in separate files, in a common directory, and include them in your script, but be careful, too much include files messes up everything. Be smart. :-)

    -- tune

Re: A question about the best way to use subroutines
by arhuman (Vicar) on Mar 25, 2001 at 14:21 UTC
    I usually only take care of code size...
    (If I want to make my code more readable I use comment to describe what I'm saying)
    Write your flat prog and then try to use subs instead of similar part of the code.
    If using a sub reduce the number of lines it's often an obvious good choice.
    (With experience you'll see that you'll do it at the design stage,
    preventing you from writing more than needed...)

    Now, I sometimes make sub from not-so-similar peaces of code
    (using a added parampeters to achive sligltly different process on different data)
    but it's not usually very good for the readablity.
    I remember I wrote long ago a sub (which did and return severals (REALLY DIFFERENT)
    things depending on the parameters, I saved some lines but it was an horror to read and debug...)

    UPDATE :
    Ooops I forgot to mention another important use for subroutines : I al so use them to make further modification easier.
    I sometimes use sub when it doesn't really reduce my code size but when it enables me to modify the logical function at one place rather at thousand places in the code.


    "Trying to be a SMART lamer" (thanx to Merlyn ;-)
Re: A question about the best way to use subroutines
by larsen (Parson) on Mar 25, 2001 at 15:26 UTC
    I think subroutines are supposed to be not only a shortcut when a piece of code is used a lot of times.

    First of all, they are linguistic tools to hide things. In other words, to build abstractions. I think programs are built on abstractions.

    So you could use subroutines even if a piece of code is used only once.It will increases readability, and maybe you will easier reuse that piece of code.

Re: A question about the best way to use subroutines
by Desdinova (Friar) on Mar 27, 2001 at 03:09 UTC
    The advice given to me by an 'old' programmer to try to keep each 'function' under 50 lines so it fit on one screen. If it goes over that you can most likely split it in two items. If the main part of the script was over that then you most likely had to many subs. This of course is meant as a rough guideline. For me looking at code this way has made it very easy to break apart a program into neat parts that are often reusable with only minor tweaking. Also the whole idea of seeing the function without scrolling has made maintance easier.
Re: A question about the best way to use subroutines
by petral (Curate) on Mar 27, 2001 at 08:48 UTC
    A really general rule is "one level of abstraction".  Basically, if I'm dealing with the _details_ of one step in a routine, that becomes a subroutine.  Then in the new routine, the details become the top level.

    p

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://66946]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (2)
As of 2024-03-19 07:14 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found