The 2011 SAS Professionals Convention is to be held July 12th - 14th in Marlow. Book before June 13th at the reduced rate of £100+VAT. If you plan to go (why wouldn't you?), don't miss the early bird discount.
Sadly, for the third year running, events outside of my control mean that I won't be going. Every year I start-off determined to go, and then something crops up in the last couple of moth to prevent it. Bah!
SAS® and software development best practice. Hints, tips, & experience of interest to a wide range of SAS practitioners. Published by Andrew Ratcliffe's RTSL.eu, guiding clients to knowledge since 1993
Monday, 6 June 2011
Wednesday, 1 June 2011
NOTE: Length Functions (Something Missing?)
How many functions to tell you the length of a value do you need? At least six apparently! SAS provides LENGTH, LENGTHC, LENGTHM, LENGTHN, KLENGTH and %LENGTH. Why?...
As we've all discovered to our cost, the basic LENGTH function accurately tells us the length of a character string (excluding trailing blanks) unless the string is completely blank, in which case LENGTH misleadingly returns the value 1. That's why I always use LENGTHN; it returns the value zero for a blank string.
I rarely use the others but, for the record, LENGTHC returns the length of a string including trailing blanks; but beware because it returns the value one when supplied with a null string as input.
The LENGTHM function is a slightly different beast because it returns the declared length of the variable rather than of its contents, i.e. it returns what was specified on (or implied for) the variable's LENGTH statement. KLENGTH is another oddity. In essence, it is the DBCS equivalnet of LENGTH. And %LENGTH is the macro equivalent of LENGTHN, i.e. it returns zero for a null/blank string.
Oh, there's a %KLENGTH too. And SAS/IML has a length function too, but let's not go there!
Why might we be using length functions? One popular use is to test if a variable is missing or null. For these cases, the MISSING or NMISS functions are often the best option - not least because their names make the purpose of their usage far clearer than using a length function.
The MISSING function returns 1 if the value passed to it is missing. The value passed to it can be numeric or character. A chracter string is deemed to be missing if it is all blank or has zero length. Perfect! This is a far better choice than any of the length functions if you want to test avariable for a missing value.
NMISS returns the number of missing numeric values.
Finally, for completeness, I should mention CALL MISSING. You can use this routine to set character or numeric values to missing, though very few of us do.
As we've all discovered to our cost, the basic LENGTH function accurately tells us the length of a character string (excluding trailing blanks) unless the string is completely blank, in which case LENGTH misleadingly returns the value 1. That's why I always use LENGTHN; it returns the value zero for a blank string.
I rarely use the others but, for the record, LENGTHC returns the length of a string including trailing blanks; but beware because it returns the value one when supplied with a null string as input.
The LENGTHM function is a slightly different beast because it returns the declared length of the variable rather than of its contents, i.e. it returns what was specified on (or implied for) the variable's LENGTH statement. KLENGTH is another oddity. In essence, it is the DBCS equivalnet of LENGTH. And %LENGTH is the macro equivalent of LENGTHN, i.e. it returns zero for a null/blank string.
Oh, there's a %KLENGTH too. And SAS/IML has a length function too, but let's not go there!
Why might we be using length functions? One popular use is to test if a variable is missing or null. For these cases, the MISSING or NMISS functions are often the best option - not least because their names make the purpose of their usage far clearer than using a length function.
The MISSING function returns 1 if the value passed to it is missing. The value passed to it can be numeric or character. A chracter string is deemed to be missing if it is all blank or has zero length. Perfect! This is a far better choice than any of the length functions if you want to test avariable for a missing value.
NMISS returns the number of missing numeric values.
Finally, for completeness, I should mention CALL MISSING. You can use this routine to set character or numeric values to missing, though very few of us do.
Wednesday, 25 May 2011
NOTE: Parameter Validation - %DATATYP
In yesterday's article on coupling, I showed how the use of macro parameters can decouple your macros, making them more maintainable and reliable. Building-in some parameter validation is always a good practice too. Some simple, basic validation can sometimes be all that's needed to reveal a problem before it gets too far into your sequence of macro calls and becomes difficult to unpick and debug.
The %DATATYP autocall macro is very useful in this area. When passed a value, it will tell you whether the string is numeric or character. This is especially useful in macro-world where all values are handled as character strings. The following code snippet gives an introduction to its usage:
The macro is smart enough to recognise 1.23E3 as numeric, i.e. 1230.
An alternative DATA Step approach is to use the INPUT function with the question mark (?) or double question mark (??) modifier in order to avoid messages being written to the log.
Whatever approach you take, a little parameter validation is better than none and will undoubtedly repay you at some point in the future.
The %DATATYP autocall macro is very useful in this area. When passed a value, it will tell you whether the string is numeric or character. This is especially useful in macro-world where all values are handled as character strings. The following code snippet gives an introduction to its usage:
%macro multiplier(data=,out=,var=,mult=);
%if %datatyp(&data) ne CHAR %then...
%if %datatyp(&mult) ne NUMERIC %then...The macro is smart enough to recognise 1.23E3 as numeric, i.e. 1230.
An alternative DATA Step approach is to use the INPUT function with the question mark (?) or double question mark (??) modifier in order to avoid messages being written to the log.
/* Check that Y (a char var) contains a valid numeric value */
if input(y,??best.) eq . then
... <not numeric>Whatever approach you take, a little parameter validation is better than none and will undoubtedly repay you at some point in the future.
Tuesday, 24 May 2011
NOTE: Coupling, Bad
In my recent article on %MEND I said I didn’t like nested macro definitions. Some of my correspondents have suggested it’s a good means of keeping macro code near to where it’s called. I think this suggests a bad approach; namely, the inner workings of the sub-macro should not be relevant to the caller – the important element is the interface and outward behaviour, thus the definition of the sub-macro need not be located near to where it's used. (Plus, if it's used in more than one place it cannot possibly be located near to both.)
The design principle involved here is of "loose coupling". Design good practice suggests that objects, components and modules in your applications should make use of little or no knowledge of the internal implementation of other objects, components and modules with which they interact. Designing a loosely-coupled system provides the benefit of making it easier to make changes to one object without impacting another (if the interface and outward behaviour are not changed); this, in-turn, means your applications become more maintainable and reliable.
For example, I am loosely-coupled with my car, i.e. I have little or no understanding of how it works beyond my knowledge of its steering wheel, gear lever and pedals (its interface). I don't need any knowledge of how the engine or gearbox work in order to drive it. The advantage to me is that I can hop into almost any car and drive it just as effectively; the advantage for the car manufacturer is they can sell their cars to a wide range of people without needing to train them on how to use the specific model of car.
For macros in particular, it is very easy to write something that requires the caller to know things about the inner workings of the macro, e.g. the macro may expect certain global macro variables to be defined, or it may write its output to other global macro variables, or it may read/write to/from specific data sets. It is so much easier to define these things as part of the parameter interface for the macro, then it is so much easier to understand what the macro wants as input and what it might provide as output. Consider these two macros:
The second, with the clear parameter interface, does not demand that its user knows the names of the input and output data sets; the interface and function of the macro are already clear. Thus it is easier to enhance the macro without "breaking" any code that uses it.
The example is just a simple one, but the principle has greater and greater value as your applications and their components get larger. The topic is much bigger than I can describe here. It's difficult finding references that don't go into (non-SAS) coding examples. If you're brave, you can try Martin Fowlers' classic Reducing Coupling from 2002; else take a look at Coupling and Cohesion in the C2 wiki.
The design principle involved here is of "loose coupling". Design good practice suggests that objects, components and modules in your applications should make use of little or no knowledge of the internal implementation of other objects, components and modules with which they interact. Designing a loosely-coupled system provides the benefit of making it easier to make changes to one object without impacting another (if the interface and outward behaviour are not changed); this, in-turn, means your applications become more maintainable and reliable.
For example, I am loosely-coupled with my car, i.e. I have little or no understanding of how it works beyond my knowledge of its steering wheel, gear lever and pedals (its interface). I don't need any knowledge of how the engine or gearbox work in order to drive it. The advantage to me is that I can hop into almost any car and drive it just as effectively; the advantage for the car manufacturer is they can sell their cars to a wide range of people without needing to train them on how to use the specific model of car.
For macros in particular, it is very easy to write something that requires the caller to know things about the inner workings of the macro, e.g. the macro may expect certain global macro variables to be defined, or it may write its output to other global macro variables, or it may read/write to/from specific data sets. It is so much easier to define these things as part of the parameter interface for the macro, then it is so much easier to understand what the macro wants as input and what it might provide as output. Consider these two macros:
| No parameter interface | Clear parameter interface |
|---|---|
%macro demo;Called thus: | %macro doubler(data=,out=,var=);Called thus: %doubler(data=first ,out=second ,var=profit); |
The second, with the clear parameter interface, does not demand that its user knows the names of the input and output data sets; the interface and function of the macro are already clear. Thus it is easier to enhance the macro without "breaking" any code that uses it.
The example is just a simple one, but the principle has greater and greater value as your applications and their components get larger. The topic is much bigger than I can describe here. It's difficult finding references that don't go into (non-SAS) coding examples. If you're brave, you can try Martin Fowlers' classic Reducing Coupling from 2002; else take a look at Coupling and Cohesion in the C2 wiki.
Monday, 23 May 2011
A New Look
If you're a regular visitor to the NOTE: web site you'll instantly notice that it looks different. NOTE:'s 2nd birthday is approaching (in July) so we thought it was time for a make-over. We're pleased with the new, brighter appearance. Please tell us what you think...
Software Practice Advancement (BCS SPA)
I recently started a new contract in London. Having been based outside of London for the last couple of years, I'm being reminded of the benefits of working in the metropolis. One benefit is that I get to use public transport, thereby increasing my reading time; the second is that I am able to regularly attend the BCS SPA meeting on the first Wednesday of each month.
The British Computer Society (BCS) has many specialist sub-groups. One of these is the Software Practice Advancement (SPA) group. BCS SPA's aim is to
Recent events include:
The British Computer Society (BCS) has many specialist sub-groups. One of these is the Software Practice Advancement (SPA) group. BCS SPA's aim is to
Share knowledge and experience about best and emerging practices for software development. In particular the group is concerned with good and efficient design, the positioning of new technologies, and the promotion of reflective, inclusive and balanced processes.The group holds an event on the first Wednesday of every month, each with a guest speaker. These events are free to attend, and you don't need to be a member of the BCS nor SPA. I shall be attending June's event on the subject of estimation. As ever, the event will be preceded by complimentary sandwiches, and followed by beer in a nearby hostelry (usually the Coal Hole). If you are coming too, let me know.
Recent events include:
- January - Thomas Power, CEO of Ecademy: From CSC to ORS - Recent Business Ideas on Social Marketing
- February - Ed Seymour of Fujitsu UK: Agility and Quality in Software Development - The APT Approach
- March - Barry Varley of ACUTEST: Successful delivery when you have no time to test
- April - Benjamin Mitchell: Beyond Agile? Ideas & Experiences from Industry
- May - How to design a flexible platform architecture: Lessons learned from the development of the Jazz platform
Subscribe to:
Posts (Atom)