С наступающим/наступившим вас праздником! Сегодня вашему вниманию
предлагается праздничное дополнение к 41-му выпуску рассылки, посвящённое временным
переменным.
Дополнение к 41-му выпуску от 22.10.2006
В 41-м выпуске рассылки мы коснулись некоторых вопросов работы
с временными (scratch) переменными. Мы отметили, что они являются эффективным
вспомогательным средством разработки программ на синтаксисе SPSS, поскольку
не записываются в файл данных и исчезают после завершения преобразований проходом
по данным. По этой причине, например, часто временные переменные используются
в качестве индексов в циклах. Для этой цели можно использовать и постоянные
переменные, но в последнем случае после преобразований в файле данных у вас
появится лишний(е) столбец(цы), содержащий(е) последнее(ие) значение(я) индексной(ых)
переменной(ых) для каждого наблюдения. Простейший пример:
COMPUTE a=1.
LOOP i=1 to 10.
- COMPUTE a=a*i.
END LOOP.
запишет для каждого наблюдения в переменную a значение
10! (10-факториал), равное 3628800. Но, вместе с этим, ненужная, вообще говоря,
переменная i будет содержать для каждого наблюдения число 11, т.е.
последнее значение индекса, вышедшее за верхнюю границу (для данного значения
цикл уже не выполнялся).
Использование временного аналога переменной i (например,
#i) приведёт к тем же результатам, но без появления лишней переменной
в файле (будете пробовать - не забудьте удалить старые переменные i
и a из файла):
COMPUTE a=1.
LOOP #i=1 to 10.
- COMPUTE a=a*#i.
END LOOP.
Если говорить об индексных переменных в цикле LOOP, то на этом
различия между временными и постоянными переменными обычно заканчиваются. Временные
переменные не подвергаются реинициализации при переходе к следующему наблюдению,
но это никак не влияет на работу LOOP, поскольку этот цикл всегда выполняется
лишь в рамках одного и того же наблюдения. Из этого правила, однако же, есть
исключение, на которое мне недавно любезно указали в форуме SPSSX-L (см. исходное
сообщение здесь;
Автор сообщения: Richard
Ristow).
В том случае, если цикл LOOP помещается внутри процедуры чтения
данных (INPUT PROGRAM), конец очередного наблюдения (инструкция END CASE) может
находиться внутри цикла LOOP. В некоторых случаях это приведёт к разным результатам,
в зависимости от того, используем ли мы постоянные, или временные переменные
в качестве индекса. Сравните два варианта:
первый...
INPUT PROGRAM.
LOOP i=1 to 10.
- LOOP j=1 to 20.
- COMPUTE a=i*j.
- END CASE.
- END LOOP.
END LOOP.
END FILE.
END INPUT PROGRAM.
EXECUTE.
...и второй...
INPUT PROGRAM.
LOOP #i=1 to 10.
- LOOP #j=1 to 20.
- COMPUTE a=#i*#j.
- END CASE.
- END LOOP.
END LOOP.
END FILE.
END INPUT PROGRAM.
EXECUTE.
Первый вариант, как будто, не даёт тех результатов, которые мы
от него ожидали. Это связано с тем, что внутри второго (вложенного) цикла LOOP
стоит инструкция перехода к следующему наблюдению, в результате чего происходит
реинициализация всех переменных. Переменной j, как индексу второго
цикла, присваивается очередное значение, но переменная i, поскольку
возвращения в первый цикл не происходит, инициализируется в системное пропущенное
значение (SYSMIS), что и объясняет получаемый результат. Сбоя в исполнении внешнего
цикла всё же не происходит, так как, вероятно, за "видимой" переменной
"i" скрывается её второе "я" в форме системной
переменной - счётчика цикла (подробнее об этом можно почитать в одном из сообщений
(и связанных с ним сообщениях) того же форума SPSSX-L).
Второй пример сработал отлично, поскольку индексные переменные
в данном случае реинициализации не подвергаются и сохраняют свои значения до
следующего обновления.
К слову, первый вариант программы (с постоянными переменными)
можно легко исправить, добавив инструкцию LEAVE, подавляющую реинициализацию
отдельных переменных: