/* Author: Paul H. Cook, 2002. /* E-mail: me _at_ myhomeaddress _dot_ org (replace _at_ and _dot_) /* This document demonstrates how to perform arithmetic in SPSS macros. /* Arithmetic is not implemented in SPSS macros. Assignments such as /* "!let !x = !y + 3" generate an error. Two solutions to this omission /* are demonstrated in this document: using strings, and using loops. /* String manipulations can be used to simulate arithmetic. Concatenation /* can be used to perform addition, and substrings can be used to perform /* subtraction. Multiplication and division can be implemented as repeated /* addition and subtraction. /* The looping construct in SPSS macros implements true arithmetic. The /* indexing variable is incremented and decremented using integer /* arithmetic. This functionality can be harnessed and used. /* In this document, macro !demo() demonstrates: /* 1) addition, using strings /* 2) addition, using loops /* 3) multiplication, using strings /* 4) multiplication, using loops /* 5) subtraction, using strings /* 6) subtraction, using loops /* 7) division, using strings /* 8) division, using loops /* 9) division, using a hybrid approach /* 10) modulus, using strings /* 11) modulus, using loops /* 12) modulus, using a hybrid approach /* If you want to perform addition, subtraction or multiplication, then the /* most clear and concise method is that using strings. /* If you want to perform division or calculate the modulus, then the most /* clear and concise method is the hybrid approach using both the loops /* method and the strings method. /* The most common scenario for using arithmetic is to increment or decrement /* a variable by a fixed amount. These can be accomplished with single lines /* of code when using the strings methodology: /* - to increment !counter by 1, use... /* !let !counter = !length(!concat(!blanks(!counter), !blanks(1))) /* - to decrement !counter by 1, use... /* !let !counter = !length(!substr(!blanks(!counter), 2)) /* This demonstration is implemented for whole numbers only {0, 1, 2, 3,...}. /* The methodology could probably be extended to work with negative numbers /* and decimal fractions. /* Division is implemented as integer division, such that 6/2=3 and 7/2=3. /* Methodologies for calculating the modulus (remainder) are given, such that /* the modulus of 6/2=0 and the modulus of 7/2=1. /* The looping construct in SPSS macros uses long integers, which limits the /* maximum result to +/- 2,147,483,647 when using the loops method. Please /* note that SPSS does not generate an overflow error if you exceed this; it /* just produces the wrong result. /* This code cannot be encapsulated in macros called from other macros. This /* is because assignments such as "!let !a = !add(6,2)" do not work; SPSS /* will not recognise any tokens following the macro name "!add". /************************ start of macro ************************/ define !demo() /* ------------------------------------------------------ */ /* initialise */ /* ------------------------------------------------------ */ /* The operations 6+2, 6*2, 6-2, 6/2, and the modulus of 6/2 will */ /* be demonstrated using the variables !a and !b. */ !let !a = 6 !let !b = 2 /* ------------------------------------------------------ */ /* addition, using strings (!a + !b) */ /* ------------------------------------------------------ */ /* add !a and !b */ !let !answer = !length(!concat(!blanks(!a), !blanks(!b))) /* display the answer */ compute add_str = !answer. /* ------------------------------------------------------ */ /* addition, using loops (!a + !b) */ /* ------------------------------------------------------ */ /* add !a and !b */ !let !first = true !do !x = !a !to 2147483647 !by !b !if (!first = true) !then !let !first = false !else !let !answer = !x !break !ifend !doend /* display the answer */ compute add_loop = !answer. /* ------------------------------------------------------ */ /* multiplication, using strings (!a * !b) */ /* ------------------------------------------------------ */ /* repeated addition of !b */ !let !tally = !blanks(0) !do !x = 1 !to !a !let !tally = !concat(!tally, !blanks(!b)) !doend /* record the answer */ !let !answer = !length(!tally) /* free the memory */ !let !tally = !blanks(0) /* display the answer */ compute mul_str = !answer. /* ------------------------------------------------------ */ /* multiplication, using loops (!a * !b) */ /* ------------------------------------------------------ */ /* multiply !a and !b */ !let !answer = 0 !do !y = 1 !to !a !let !first = true !do !x = !answer !to 2147483647 !by !b !if (!first = true) !then !let !first = false !else !let !answer = !x !break !ifend !doend !doend /* display the answer */ compute mul_loop = !answer. /* ------------------------------------------------------ */ /* subtraction, using strings (!a - !b) */ /* ------------------------------------------------------ */ /* subtract !b from !a */ !let !answer = !length(!substr(!blanks(!a), !length(!concat(!blanks(!b), !blanks(1))))) /* display the answer */ compute sub_str = !answer. /* ------------------------------------------------------ */ /* subtraction, using loops (!a - !b) */ /* ------------------------------------------------------ */ /* subtract !b from !a */ !let !first = true !do !x = !a !to -2147483647 !by -!b !if (!first = true) !then !let !first = false !else !let !answer = !x !break !ifend !doend /* display the answer */ compute sub_loop = !answer. /* ------------------------------------------------------ */ /* division, using strings (!a / !b) */ /* ------------------------------------------------------ */ /* if we don't have a division-by-zero error, then do the division */ !if (!b <> 0) !then /* count how many times !b goes into !a */ !let !tally = !blanks(0) !let !num = !blanks(!a) !do !x = 1 !to !a /* NB "!if (10 < 2) !then" evaluates to True, because SPSS does a string comparison. */ /* Hence, to do a numerical comparison "!if (!length(!num) < !b) !then"... */ !if (!length(!length(!num)) = !length(!b) !and !length(!num) < !b !or !length(!length(!num)) < !length(!b) ) !then /* done */ !break !else /* subtract !b from !num */ !if (!length(!num) = !b) !then !let !num = !blanks(0) !else !let !num = !substr(!num, !length(!concat(!blanks(!b), !blanks(1)))) !ifend /* increment the tally */ !let !tally = !concat(!tally, !blanks(1)) !ifend !doend /* record the answer */ !let !answer = !length(!tally) /* free the memory */ !let !tally = !blanks(0) !let !num = !blanks(0) /* display the answer */ compute div_str = !answer. !ifend /* ------------------------------------------------------ */ /* division, using loops (!a / !b) */ /* ------------------------------------------------------ */ /* if we don't have a division-by-zero error, then do the division */ !if (!b <> 0) !then !let !answer = 0 /* count how many times !b goes into !a */ !do !y = !a !to !b !by -!b /* increment the answer */ !let !first = true !do !x = !answer !to 2147483647 !if (!first = true) !then !let !first = false !else !let !answer = !x !break !ifend !doend !doend /* display the answer */ compute div_loop = !answer. !ifend /* ------------------------------------------------------ */ /* division, using a hybrid approach (!a / !b) */ /* ------------------------------------------------------ */ /* if we don't have a division-by-zero error, then do the division */ !if (!b <> 0) !then !let !answer = 0 /* count how many times !b goes into !a */ !do !y = !a !to !b !by -!b /* increment the answer */ !let !answer = !length(!concat(!blanks(!answer), !blanks(1))) !doend /* display the answer */ compute div_hyb = !answer. !ifend /* ------------------------------------------------------ */ /* modulus of !a / !b, using strings */ /* ------------------------------------------------------ */ /* if we don't have a division-by-zero error... */ !if (!b <> 0) !then /* do division */ !let !num = !blanks(!a) !do !x = 1 !to !a /* NB "!if (10 < 2) !then" evaluates to True, because SPSS does a string comparison. */ /* Hence, to do a numerical comparison "!if (!length(!num) < !b) !then"... */ !if (!length(!length(!num)) = !length(!b) !and !length(!num) < !b !or !length(!length(!num)) < !length(!b) ) !then /* done */ !break !else /* subtract !b from !num */ !if (!length(!num) = !b) !then !let !num = !blanks(0) !else !let !num = !substr(!num, !length(!concat(!blanks(!b), !blanks(1)))) !ifend !ifend !doend /* record the answer */ !let !answer = !length(!num) /* free the memory */ !let !num = !blanks(0) /* display the answer */ compute mod_str = !answer. !ifend /* ------------------------------------------------------ */ /* modulus of !a / !b, using loops */ /* ------------------------------------------------------ */ /* if we don't have a division-by-zero error... */ !if (!b <> 0) !then !let !answer = 0 /* do division */ !do !y = !a !to !b !by -!b !doend /* modulus = !y - !b */ !let !first = true !do !x = !y !to 0 !by -!b !if (!first = true) !then !let !first = false !else !let !answer = !x !break !ifend !doend /* display the answer */ compute mod_loop = !answer. !ifend /* ------------------------------------------------------ */ /* modulus of !a / !b, using a hybrid approach */ /* ------------------------------------------------------ */ /* if we don't have a division-by-zero error... */ !if (!b <> 0) !then /* do division */ !do !y = !a !to !b !by -!b !doend /* modulus = !y - !b !if (!y = !b) !then !let !answer = 0 !else !let !answer = !length(!substr(!blanks(!y), !length(!concat(!blanks(!b), !blanks(1))))) !ifend /* display the answer */ compute mod_hyb = !answer. !ifend !enddefine. /************************* end of macro *************************/ /* Create a data set, and run the macro. data list list /a b. begin data. 6 2 end data. !demo. execute.