/* Автор: Paul H. Cook, 2002. /* E-mail: me _at_ myhomeaddress _dot_ org (замените _at_ и _dot_) /* Этот документ демонстрирует проведение арифметических действий с /* макропеременными SPSS. /* Арифметика в макросах SPSS не предусмотрена. Выражения, вроде /* "!let !x = !y + 3" генерируют ошибку. Здесь представлены два /* пути обхождения этого препятствия: через строки и через циклы. /* Строки можно использовать для симулирования арифметических действий. /* Конкатенацию - для сложения, выделение подстрок - для вычитания. /* Умножение и деление могут выполняться как повторные сложения и вычитания. /* С помощью циклических конструкций в SPSS можно выполнять настоящие /* арифметические действия. Дело в том, что индексная переменная увеличивается и /* уменьшается с использованием целочисленных арифметических действий. Эту /* функциональность можно обуздать и использовать в своих интересах. /* В этом документе макрос !demo() демонстрирует следующее: /* 1) сложение, используя строки /* 2) сложение, используя циклы /* 3) умножение, используя строки /* 4) умножение, используя циклы /* 5) вычитание, используя строки /* 6) вычитание, используя циклы /* 7) деление, используя строки /* 8) деление, используя циклы /* 9) деление, используя смешанный подход /* 10) остаток от деления, используя строки /* 11) остаток от деления, используя циклы /* 12) остаток от деления, смешанный подход /* Если вам нужно сложение, вычитание или умножение, тогда наиболее понятный и /* лаконичный способ - с использованием строк. /* Если вам нужно разделить или вычислить остаток от деления, тогда лучший способ - /* смешанный подход, использующий как циклы, так и строки. /* Основная операция в арифметических вычислениях - уменьшить или увеличить /* переменную на некоторую фиксированную величину. Это можно сделать единственной /* строкой кода, если используется подход со строками: /* - чтобы увеличить переменную !counter на 1, используйте... /* !let !counter = !length(!concat(!blanks(!counter), !blanks(1))) /* - чтобы уменьшить переменную !counter на 1, используйте... /* !let !counter = !length(!substr(!blanks(!counter), 2)) /* Эта программа работает с целыми неотрицательными числами {0, 1, 2, 3,...}. /* В принципе, эта методология может быть расширена для работы с отрицательными и /* дробными числами.. /* Здесь выполняется целочисленное деление, например 6/2=3 и 7/2=3. /* Даётся способ вычисления остатка, так, что остаток от деления 6/2=0 и остаток 7/2=1. /* Циклические конструкции в макросах SPSS основаны на индексах типа длинное целое, /* что ограничиваем максимальный результат числом +/- 2,147,483,647, если мы /* используем метод циклов для вычислений. Обратите внимание, что SPSS не генерирует /* при достижении лимита ошибку переполнения, а просто возвращает неправильный /* результат. /* Данный код не может быть встроен в макрос, вызываемый из другого макроса. Это /* оттого, что код вида, например, "!let !a = !add(6,2)" не сработает. /* SPSS не распознает никакие символы, следующие за именем макроса "!add". /************************ начало макроса ************************/ define !demo() /* ------------------------------------------------------ */ /* инициализация */ /* ------------------------------------------------------ */ /* Операции 6+2, 6*2, 6-2, 6/2, и остаток от деления 6/2 будут */ /* продемонстрированы с использованием переменных !a и !b. */ !let !a = 3 !let !b = 2 /* ------------------------------------------------------ */ /* сложение, использую строки (!a + !b) */ /* ------------------------------------------------------ */ /* сложить !a и !b */ !let !answer = !length(!concat(!blanks(!a), !blanks(!b))) /* вывести ответ */ compute add_str = !answer. /* ------------------------------------------------------ */ /* сложение, используя циклы (!a + !b) */ /* ------------------------------------------------------ */ /* сложить !a и !b */ !let !first = true !do !x = !a !to 2147483647 !by !b !if (!first = true) !then !let !first = false !else !let !answer = !x !break /* Идея здесь в том, что первый раз цикл выполняется с начальным индексом !a, */ /* а потом, с индексом !a + шаг, равный !b. Во второй раз значение !first уже равно */ /* false и цикл вылетает с результатом сложения */ !ifend !doend /* вывести ответ */ compute add_loop = !answer. /* ------------------------------------------------------ */ /* умножение, используя строки (!a * !b) */ /* ------------------------------------------------------ */ /* повторное сложение аргумента !b */ !let !tally = !blanks(0) !do !x = 1 !to !a !let !tally = !concat(!tally, !blanks(!b)) !doend /* запоминание ответа */ !let !answer = !length(!tally) /* освобождение памяти */ !let !tally = !blanks(0) /* вывести ответ */ compute mul_str = !answer. /* ------------------------------------------------------ */ /* умножение, используя циклы (!a * !b) */ /* ------------------------------------------------------ */ /* умножить !a на !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 /* вывести ответ */ compute mul_loop = !answer. /* ------------------------------------------------------ */ /* вычитание, используя строки (!a - !b) */ /* ------------------------------------------------------ */ /* вычесть !b из !a */ !let !answer = !length(!substr(!blanks(!a), !length(!concat(!blanks(!b), !blanks(1))))) /* вывести ответ */ compute sub_str = !answer. /* ------------------------------------------------------ */ /* вычитание, используя циклы (!a - !b) */ /* ------------------------------------------------------ */ /* вычесть !b из !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 /* вывести ответ */ compute sub_loop = !answer. /* ------------------------------------------------------ */ /* деление, используя строки (!a / !b) */ /* ------------------------------------------------------ */ /* если делитель - не ноль, выполняем деление */ !if (!b <> 0) !then /* считаем, сколько раз !b входит в !a */ !let !tally = !blanks(0) !let !num = !blanks(!a) !do !x = 1 !to !a /* NB "!if (10 < 2) !then" оценивается как Истина, так как SPSS делает строковое сравнение. */ /* Если нужно сравнение чисел, надо использовать "!if (!length(!num) < !b) !then"... */ !if (!length(!length(!num)) = !length(!b) !and !length(!num) < !b !or !length(!length(!num)) < !length(!b) ) !then /* сделано*/ !break !else /* вычитаем !b из !num */ !if (!length(!num) = !b) !then !let !num = !blanks(0) !else !let !num = !substr(!num, !length(!concat(!blanks(!b), !blanks(1)))) !ifend /* наращиваем счётчик вхождений tally */ !let !tally = !concat(!tally, !blanks(1)) !ifend !doend /* запоминаем ответ */ !let !answer = !length(!tally) /* освобождаем память */ !let !tally = !blanks(0) !let !num = !blanks(0) /* выводим ответ */ compute div_str = !answer. !ifend /* ------------------------------------------------------ */ /* деление, использую циклы (!a / !b) */ /* ------------------------------------------------------ */ /* если делитель - не ноль, выполняем деление */ !if (!b <> 0) !then !let !answer = 0 /* подсчёт сколько раз !b входит в !a */ !do !y = !a !to !b !by -!b /* наращиваем ответ */ !let !first = true !do !x = !answer !to 2147483647 !if (!first = true) !then !let !first = false !else !let !answer = !x !break !ifend !doend !doend /* выводим ответ */ compute div_loop = !answer. !ifend /* ------------------------------------------------------ */ /* деление с использованием смешанного подхода (!a / !b) */ /* ------------------------------------------------------ */ /* если делитель - не ноль, выполняем деление */ !if (!b <> 0) !then !let !answer = 0 /* подсчёт числа вхождений !b в !a */ !do !y = !a !to !b !by -!b /* наращиваем ответ */ !let !answer = !length(!concat(!blanks(!answer), !blanks(1))) !doend /* выводим ответ */ compute div_hyb = !answer. !ifend /* ------------------------------------------------------ */ /* остаток от деления !a / !b, используя строки */ /* ------------------------------------------------------ */ /* если делитель - не ноль... */ !if (!b <> 0) !then /* делим */ !let !num = !blanks(!a) !do !x = 1 !to !a /* NB "!if (10 < 2) !then" оценивается как Истина, так как SPSS делает строковое сравнение. */ /* Если нужно сравнение чисел, надо использовать "!if (!length(!num) < !b) !then"... */ !if (!length(!length(!num)) = !length(!b) !and !length(!num) < !b !or !length(!length(!num)) < !length(!b) ) !then /* готово */ !break !else /* вычитаем !b из !num */ !if (!length(!num) = !b) !then !let !num = !blanks(0) !else !let !num = !substr(!num, !length(!concat(!blanks(!b), !blanks(1)))) !ifend !ifend !doend /* запоминаем ответ */ !let !answer = !length(!num) /* освобождаем память */ !let !num = !blanks(0) /* выводим ответ */ compute mod_str = !answer. !ifend /* ------------------------------------------------------ */ /* остаток от деления !a / !b, используя циклы */ /* ------------------------------------------------------ */ /* если делитель - не ноль... */ !if (!b <> 0) !then !let !answer = 0 /* делим */ !do !y = !a !to !b !by -!b !doend /* остаток равен = !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 /* выводим ответ */ compute mod_loop = !answer. !ifend /* ------------------------------------------------------ */ /* остаток от деления !a / !b, используя смешанный подход */ /* ------------------------------------------------------ */ /* если делитель - не ноль... */ !if (!b <> 0) !then /* делим */ !do !y = !a !to !b !by -!b !doend /* остаток = !y - !b !if (!y = !b) !then !let !answer = 0 !else !let !answer = !length(!substr(!blanks(!y), !length(!concat(!blanks(!b), !blanks(1))))) !ifend /* выводим ответ */ compute mod_hyb = !answer. !ifend !enddefine. /************************* конец макроса *************************/ /* Создадим набор данных и запустим макрос. data list list /a b. begin data. 6 2 end data. !demo. execute. /* NB! Набор данных нужен лишь для того, чтобы сработали команды compute */ /* в выводе результатов работы макроса. */ /* Данный макрос берёт значения переменных не из файла (переменные a и b), а */ /* из тела самого макроса (см. в начале макроса) - А.Б. */