Phil Hearn: Blogger, Writer & Founder of MRDC Software Ltd.

Improving MRDCL productivity by using functions and subroutines

How to automate things that are boring to script that you want to re-use

When you get a series of support requests where the recipients of the support reply something like “thanks, but that’s quite tedious to script”, you realise you need to show someone a better way. For each of these support requests, we provided a solution using a function or subroutine that brought a reply which was more like “hey, that’s fantastic, that’s so easy”. So, in this blog article, we are going to look at writing your own reusable functions and subroutines so that you can improve your productivity.

When do functions and subroutines help?

Like a lot of the tools in MRDCL, the productivity features come into play when you are either trying to script something complex or something that is, perhaps, quite easy, but which is laborious to prepare. This blog article is going to give some practical examples of how laborious tasks can be simplified by building functions and subroutines that can be re-used either within the same project or within any number of projects – and, even, shared with colleagues.

What are functions and subroutines?

Functions and subroutines allow you to pass parameters to a fixed piece of script so that MRDCL generates whatever you need. Now, that sounds complicated, but what it means is that if you have something similar to something else that you have scripted, you can tell MRDCL what is different on the second and subsequent occasions without re-scripting the whole thing again. You can do this by passing parameters (or the differences) to the function or subroutine.

Let’s look at a simple example

Let’s say that you have a numeric field which stores the number of cans consumed of various soft drinks. Let’s also imagine that 999 denotes that the respondent doesn’t know how many cans have been consumed rather than 999 meaning that they drunk 999 cans! We could enter code something like this:

Di $brand1=$12-14,

If $12-14/999,di $brand1=u,

Xt=’Cans of Sparkle consumed’,

Di $brand2=$15-17,

If $15-17/999,di $brand2=u,

Xt=’Cans of Jumpo consumed’,

Di $brand3=$18-20,

If $18-20/999,di $brand3=u,

Xt=’Cans of Fizzi consumed’,

Etc.

Using a function instead of writing repetitive code

Now, in fact, for this example, we could have written a simple *DO loop as there is a pattern to the variable names (brand1, brand2, brand3) and the data locations (12-14, 15-17, 18-20). But, let’s use a function by passing parameters. Let’s pass the variable name, the start data location and the text for the brand to the function. We can do this in a data statement.

[*data cans=brand1,12,Sparkle]

We can then write a function that will take these three parameters and process what we want. We will need to put this into a separate .stp file, and it might be advisable to store the .stp file in a common area so that anyone can use it. You might build a file called cans.stp which processes these three parameters.

[*set col=cans.2]

Di $[cans.1]=$[col]-[col+2],

If $[col]-[col+2]/999,di $[cans.1]=u,

Xt=’Cans of [cans.3] consumed’,

Our script now could appear as:

[*data cans=brand1,12,Sparkle][*insert z:\cans.stp]

[*data cans=brand2,15,Jumpo][*insert z:\cans.stp]

[*data cans=brand3,18,Fizzi][*insert z:\cans.stp]

This function would produce the same code as above. In other words:

Di $brand1=$12-14,

If $12-14/999,di $brand1=u,

Xt=’Cans of Sparkle consumed’,

Di $brand2=$15-17,

If $15-17/999,di $brand2=u,

Xt=’Cans of Jumpo consumed’,

Di $brand3=$18-20,

If $18-20/999,di $brand3=u,

Xt=’Cans of Fizzi consumed’,

Extending this concept

The example above is quite a simple one and not especially time-saving, but let’s look into some examples where there are real productivity gains. One example might be where you want a top-two box and a bottom two box generated. The script isn’t especially difficult but takes time to prepare. Let’s say we have a variable called q2, and we want a top-two box and a bottom-two box added in a variable called q2_t2b2. The parameter that is needed and the function might like this:

[*data topbot=q2][*insert top2bot2]

[*data topbot=q6a][*insert top2bot2]

The file top2bot2.stp, which you might put in a common area on your server, might look like this:

Dm $[topbot.1]_t2b2=$[topbot.1],$[topbot.1]/1..2,4..5,

X=$[topbot.1] + ‘Top 2 Box;Bottom 2 Box’,

Xt=$[topbot.1],

Extending the function

You could extend the function so that you could choose whether the top-two box and the bottom-two box came first, at the end or in some other way. You could set a numeric parameter which indicated what you wanted. For example, 1=put at the end, 2=put at the top. Your function would then need another parameter, and your script would need to be conditional on the setting of that extra parameter. For example,

[*data topbot=q2,1][*insert top2bot2]

[*data topbot=q6a,2][*insert top2bot2]

The file top2bot2.stp would need to be extended to deal with each option:

[*set version=topbot.2]

[*sk 99 on version.ne.1]

Dm $[topbot.1]_t2b2=$[topbot.1],$[topbot.1]/1..2,4..5,

X=$[topbot.1] + ‘Top 2 Box;Bottom 2 Box’,

Xt=$[topbot.1],

[*99]

[*sk 99 on version.ne.2]

Dm $[topbot.1]_t2b2=$[topbot.1]/1..2,4..5, $[topbot.1],

X=‘Top 2 Box;Bottom 2 Box’ + $[topbot.1],

Xt=$[topbot.1],

[*99]

Fewer errors, easier to use

There is no limit to the number of parameters you can set in this way and pass to a function, and the real benefit is that you are more likely to generate error-free script.

Other examples

You might write a function to produce summary tables, or you might produce a function for each type of summary table that you ever need. You might have some common ranges that you put numbers into, such as percentages. You might want to use a function to put values into ranges of 0,1..20,21..40,41..60,61..80 and 81..100. The examples are limitless and will vary from user to user.

Using subroutines

Less known in MRDCL is the ability to write functions. They work on the same principles but must appear before their use in the script. Subroutines begin with a *SBDEF statement and end with [*SBEND] For example, you could define a subroutine like this:

[*sbdef snRepeat]

[*set RepeatCalls = RepeatCalls + 1]

! this subroutine has been called [RepeatCalls] times

[*sbend snRepeat]

You could then invoke the subroutine like this:

[*set RepeatCalls = 0]

[*sbcall snRepeat]

[*sbcall snRepeat]

This would produce the following script:

! this subroutine has been called 1 times

! this subroutine has been called 2 times

Using subroutines

You can not only pass values to a subroutine, but you can also pass *DATA statement lists and EPS files or worksheets.

Take this example:

[*sbdef snReturn=RETspdsDataSet,RETspWhich,RETspValue]

[*set RETspValue=[RETspdsDataSet.RETspWhich]]

[*sbend snReturn]

[*data dsMyList=1,5,7][*set MyValue=0][*set MyWanted=2]

[*sbcall snReturn=dsMyList,MyWanted,MyValue]

! Value set to [MyValue]

will output –

! Value set to 5

Save time, save money

This example is undoubtedly more complex and needs more advanced knowledge to use, but the examples hopefully illustrate what is possible and how you can avoid writing the same code over and over again.

Need help?

If you need help with producing a function or subroutine, please ask us for assistance. We do undertake the writing of custom functions if you wish us to provide a commercial service.