Why pass values to a subroutine?

Started by jtrask, July 15, 2015, 07:17:02 AM

Previous topic - Next topic

jtrask

What is the purpose of passing values to a subroutine?  For instance, when creating a dynamic dialog callback subroutine, it passes a number of values from the dialog.  Isn't the point of using a subroutine over a function so that it will have access to these values anyway?  Is this a 'belt and suspenders' type thing?

td

"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

DAG_P6

Quote from: td on July 15, 2015, 12:54:14 PM
It's a software design thing.

Since a subroutine sees all script level variables (that is, all variables except those defined within a User Defined Function, it may initially seem illogical to pass variables into a subroutine. Nevertheless, the ability to pass data into a subroutine through its arguments gives you a degree of flexibility that cannot be easily duplicated using nothing but the global variables.

For example, suppose that your main script defines ten variables, each of which holds the diameters of ten objects, which, under various circumstances, could be circles, spheres, or ovals whose narrow dimension is some fraction of the diameter. So, we have the following.

  • ObjectType determines whether the object at the moment is a Circle, an Oval, or a Sphere, denoted as C, O, and S, respectively.
  • ShortToLong is a floating point number that is multiplied by a diameter to get the length of the short dimension if ObjectType = "O" and is ignored otherwise.
  • LongDimension1 through LongDimension10 denote the long dimension when operating in Oval mode, and the diameter when operating in Circle and Sphere modes.
Finally, suppose that you have a subroutine, Volume, that computes the volume of the object. In Circle and Oval modes, it returns an Area, while it returns a true volume in Sphere mode.

There are at least three ways to code the Volume subroutine.

  • Define ten script variables, ObjectVolume1 through ObjectVolume10 Call the subroutine without arguments, and use a switch block to to compute the volumes of all ten at once.
  • Call the subroutine with one argument, WhichObject, and have the switch block use it to decide which of the ten LongDimension variables to use. Presumably, this subroutine returns the volume, and you store it into one variable for subsequent processing.
  • Call the subroutine with one argument, LongDimension, which, in combination with the ObjectType script variable, and ShortToLong, if needed, returns the area or volume of the specified type of object, given its LongDimension.
Of the three, the third subroutine is by far the simplest to write, because it has only to use a simple Switch block to evaluate ObjectType, and feed the remaining variables into the appropriate formula.

Since nobody is likely to do any of the above, because it is even more simple, not to mention robust, to define three functions, AreaOfCircle, AreaOfOvel, and VolumeOfSphere, the above example is completely contrived to illustrate a point.

I cannot, in good conscience, leave the subject of subroutines without reminding lurkers of the inherent risks of using subroutines. Since a subroutine can see and change any script variable, you must code carefully to avoid accidentally changing the wrong variable, thereby introducing a bug that might not show up right away, and could be challenging to diagnose and correct when it rears its ugly head, probably when you can least afford to stop and deal with it. In the 20 years or so since user defined functions became part of the language, I can think of only one or two cases when I chose a subroutine over a function. Functions are so much safer, easier to write and test, and there are very few circumstances when I can justify the significantly increased risk that subroutines bring to the party.
David A. Gray
You are more important than any technology.

stanl

Ditto....  I use SubRoutine parms with all of my COM/NET apps.  For example, I would have scripts with multiple connections and several subs that depend on which connection is passed to them. DefineFunction does not always lend itself in these cases where some flexibility is needed.

td

My favorite use of WIL (a.k.a., WinBatch) subroutines is as a performance enhancer of a specific types of CPU bound algorithm.  In algorithms that are implemented using one or more tight looping structures with largish blocks of conditionally executed code in those loops, placing the conditionally executed blocks in subroutines will produce dramatic performance improvements.  This effect is a WIL specific optimization and cannot be generalized to other scripting languages.   
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

DAG_P6

Interesting. Can you speculate about why this is so?
David A. Gray
You are more important than any technology.

td

Speculate is not necessary.  It is simply because conditional blocks are not jumped (think jne, jz, or je) in WIL.  Conditional blocks are parsed but not executed.  Using a subroutine in a block means that only one line gets parsed.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

DAG_P6

It's been a while since I stepped through a script, but I remember the line at a time parsing, now that you mention it.

Quote from: td on July 28, 2015, 09:51:24 PM
Speculate is not necessary.  It is simply because conditional blocks are not jumped (think jne, jz, or je) in WIL.  Conditional blocks are parsed but not executed.  Using a subroutine in a block means that only one line gets parsed.

So the blocks get divided up into several subroutines, with a top level routine that makes the first cut?
David A. Gray
You are more important than any technology.