Could you provide an example of where you would use such a function. Am not against it, but just don't see where to use it. If its datastep:
data have; a=1; b=2; run; data want; set have (rename=(a=b b=a)); run;
ChrisNZ wrote:
Just a simple function to replace
TMP=A;
A =B;
B =TMP;
with shorter and more legible
call swap(A,B);
and RC=swap(A,B); for macro use.
It sort of already exists.
27 data _null_;
28 input a b ;
29 put 'NOTE: Original ' (_all_)(=);
30 call allperm(2,a,b);
31 put 'NOTE: Swap ' (_all_)(=);
32 call allperm(2,a,b);
33 put 'NOTE: Back ' (_all_)(=);
34 cards;
NOTE: Original a=123 b=4565
NOTE: Swap a=4565 b=123
NOTE: Back a=123 b=4565
NOTE: Original a=8888 b=999
NOTE: Swap a=999 b=8888
NOTE: Back a=8888 b=999
I checked with internal experts (okay, just one: Rick Langston) and he says that the overhead of a CALL routine would be less efficient than a macro for something like a "swap" operation. That is, from a performance standpoint, you're better off with:
%macro swap(x,y);
temp = &x;
&x=&y;
&y = temp;
%mend;
%swap(x,y);
A test. ALLPERM does check that a and b are the same type and length.
26 data _null_;
27 input a b;
28 do _n_ = 1 to 1e8;
29 call allperm(2,a,b);
30 end;
31 cards;
NOTE: DATA statement used (Total process time):
real time 6.99 seconds
cpu time 6.97 seconds
34 ;;;;
35 run;
36
37 %macro swap(x,y);
38 temp = &x;
39 &x=&y;
40 &y = temp;
41 %mend;
42 data _null_;
43 input a b;
44 do _n_ = 1 to 1e8;
45 %swap(a,b);
MPRINT(SWAP): temp = a;
MPRINT(SWAP): a=b;
MPRINT(SWAP): b = temp;
46 end;
47 cards;
NOTE: DATA statement used (Total process time):
real time 1.64 seconds
cpu time 1.65 seconds
2 The SAS System 09:25 Wednesday, January 25, 2017
@RW9 Sometimes in some calculations, you need to swap two values around to keep going. That's not an everyday need, but it would be handy. A bit like the lag funtions are not used everyday, but is useful (much more so than my proposal) for some data manipulation.
@LinusH Not too sure about proc fcmp. Function SWAP would return nothing.
@data_null__ Thank you, I never used ALLPERM.
ALLPERM does check that a and b are the same type and length.
As far as I could see, type must be numeric and lengths don't matter.
@ChrisHemedinger Most requests here could be implemented as custom home-made functions.
These functions must then be maintained, kept online, etc. That's why people ask for their implementation in the SAS language. As for performance (and you know how that matters to me), there can be a worthwhile trade-off for legibility.
Like when using the if functions rather than tests, or the cat functions:
data _null_; * 17s;
retain A B 'aaaaaaaaaaaa';
length C $32;
do i=1 to 1e9;
C=A||B;
end;
run;
data _null_; * 21s;
retain A B 'aaaaaaaaaaaa';
length C $32;
do i=1 to 1e9;
C=cat(A,B);
end;
run;
data _null_; * 8s;
do I=1 to 1e9;
if I<10 then A=1;
else if I<11 then A=2;
else if I<12 then A=3;
else if I<13 then A=4;
else A=5;
end;
run;
data _null_; * 32s;
do I=1 to 1e9;
A=ifn(I<10, 1
,ifn(I<11, 2
,ifn(I<12, 3
,ifn(I<13, 4
, 5))));
end;
run;
They are more expensive but still worth having.
@ChrisNZ The swap variables can be character but they have to be the same length. While a data step is running all numeric variables are length 8.
25 data _null_;
26 input (a b)(:$9. :$8.);
27 put 'NOTE: Original ' (_all_)(=);
28 call allperm(2,a,b);
29 put 'NOTE: Swap ' (_all_)(=);
30 call allperm(2,a,b);
31 put 'NOTE: Back ' (_all_)(=);
32 cards;
NOTE: Original a=123 b=4565
ERROR: In a call to the ALLPERM function or routine, argument 2 has length 9, but argument 3 has length 8. These arguments must
have the same length.
ERROR: Internal error detected in function ALLPERM. The DATA step is terminating during the EXECUTION phase.
36 ;;;;
@data_null__ It seems that I didn't look and test properly. Thank you.
While a data step is running all numeric variables are length 8 => fair enough.
If you really want to do it without creating a temporary variable, and only want to do it to to integer values, then bitwise XOR swap is an option.
It is easy to encapsulate it in a macro so you can make single-line swaps in your data steps without having to later drop a variable.
%macro swap(x,y); &x = bxor(&x,&y); &y = bxor(&y,&x); &x = bxor(&x,&y); %mend; data test; a = 1; b = 2; output; a = 12.123456789; b = 50.987654321; output; run; data test2; set test; a_orig = a; b_orig = b; put a=hex.; put b=hex.; %swap(a,b); put a=hex.; put b=hex.; run;
Note that the second example of a floating point value ends up getting trucated to just the part before the decimal. That appears to just be how sas bitwise functions work.
Nifty that you can do such a thing in SAS, as it is often used as a high-speed memory efficient swap in low level programming. However, it is of limited utility given it appears to only work on integers. More of a cool trick than something you would need to do in SAS very often.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.