1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
/* Автор: 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), а */
/* из тела самого макроса (см. в начале макроса) - А.Б. */