BookmarkSubscribeRSS Feed

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.

 

 

9 Comments
LinusH
Tourmaline | Level 20

Support this, but this must be easy to implement using FCMP?

RW9
Diamond | Level 26
Diamond | Level 26

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;
data_null__
Jade | Level 19

@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
ChrisHemedinger
Community Manager

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); 
data_null__
Jade | Level 19

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

 

 

ChrisNZ
Tourmaline | Level 20

@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.

 

data_null__
Jade | Level 19

@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         ;;;;
ChrisNZ
Tourmaline | Level 20

@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. 🙂

Otto
Calcite | Level 5

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.