An Idea Exchange for SAS software and services

Comments
by Trusted Advisor
on ‎04-25-2016 05:55 PM

What about adding a character 'conversion-type' to %sysevalf to signify to treat the input as character data?  I think it would also be simpler to keep %eval as is, optionally just adding %evalC, even though it wouldn't benefit example #1

by PROC Star
on ‎04-25-2016 07:54 PM

Thanks @FriedEgg .  I agree with leaving %Eval as is.

 

My goal is to be able to specify that a boolean expression should be evaluated as a character expression vs a numeric expression.  I like %EvalC and %EvalN for that, partly because %SysEvalF() always felt like a long name for what I wish %EVAL did.  But I think I could also agree with a %SysEvalF approach, if it allowed me to specify both character and numeric evaluation.  Currently the Boolean conversion-type does both character evaluations and numeric evaluations, without allowing me to specify my intent.  Maybe adding a BooleanC type and a BooleanN type would work for me.  So:

 

%put >>%sysevalf(5.0>15,BooleanN)<<; *False- numeric comparison;
%put >>%sysevalf(5.0>15,BooleanC)<<; *True- character comparison;
%put >>%sysevalf(5.0>Q15,BooleanN)<<; *Error: character operand found where numeric expected;

by New Contributor _s_
on ‎04-25-2016 10:00 PM

I recommend allowing strong numeric and character typing of the results of an evaluation of an argument. If one knows the expected data type of the result, the more specific evaluation will prevent potentially erroneous choices by the compiler.

_S_

by Super User
on ‎04-26-2016 03:54 AM

I recommmend using Base SAS which does have constructs for each datatype, and has functions to process these, and is in its entirety what it should be used for.  Macro language is only present to generate Base SAS code.  It does nothing else, and I really don't understand this need to push Base SAS out of the picture and only use Macro language?  Its a bit like saying, sure the enhanced editor (in an ideal world) has code recognition, finds functions, runs things, shows log etc. but I have to use Word to code my programs.

by New Contributor _s_
on ‎04-26-2016 08:16 AM

The SAS Macro language has evolved as a way to operate on the computing environment of SAS/Base. For example, one may need to concatenate the values of %SysVer() and the OS to determine which compiled Macro library and Format library to reference. A relatively simple SAS Macro places the label of the global environment prior to SAS/Base program compilation.

_S_

by PROC Star
on ‎04-26-2016 08:54 AM

Agreed, @RW9, that the primary purpose of the macro language is to generate SAS code, and the DATA step language is designed to manipulate data.  Still, there are many places where even basic use of the macro language requires it to evaluate a logical expression, and it implicitly calls %EVAL.  This is done in %IF statements, %DO loops, etc.  This is the situation where I would like to be able to specify that an expression should be evaluated as a numeric expression vs character expression.

 

I'm not trying to replicate the power of the DATA step in manipulating data, but nor do I want to have to drop out of the macro language and into the DATA step language every time I want to evaluate the value of some macro variable (in order make some decision about what SAS code to generate).

 

As an example, when given two macro vars X and Y, I think it's good that we can code:

%if &x < &y %then %do ... ;

Instead of:

 

data _null_;
  call symputx("boolean",&x < &y);
run;
%if &boolean %then %do ... ;

 

 

A macro language without  %IF statements or %DO loops would not be very useful. 

 

I just want to have a way to speficy in the macro language whether the expression &x<&y should be evaulated as a numeric comparison or a character expression.

by Super User
on ‎04-26-2016 11:45 AM

If the actual issue is that your "users" can't provide clean data for values (your 793221D360 example) then before any processing the rules for the type of intended variable should be enforced. You could check the contents using %sysfunc and the various ANYALPHA ANYCNTRL ANYGRAPH ANYPUNCT or similar functions before passing to the %eval or %sysevalf functions (or places that use them implicitly such as %if)

by PROC Star
on ‎04-26-2016 12:37 PM

Thanks @ballardw.  It isn't always a problem of users entering bad values. I think it's more that as a developer, I want to state which type of evaluation (numeric or character) should be done.  For example, a user may be allowed to pass &x=10 &y=2.  I should then be able to dictate whether &x > &y does a character comparison (return false) or numeric comparison (return true).  I think the flexibility designed into %Eval and %SysEvalF to do both character and numeric evaluation is problematic.

 

Yes, I could use %ANYALPHA, or the SAS-provided %DataTyp autocall macro (copied below) to try to determine type.  But to me, that introduces another layer of guessing.  That is, I look at a text value and guess through some algorithm whether it is a number or a character string and make some conditional decision, and then call %SysEvalF which will also look at the text value and also guess whether it is a number or a character using (likely) some different algorithm and make a conditional decision.  So I still risk my algorithm guessing a string is numeric, and sending that value to %SysEvalF only to have %SysEvalF suprise me and treat it as character (without an indication in the log).

 

My goal in recommending %EvalC and %EvalN is to avoid such guessing.

 

%macro datatyp(parm);
%*********************************************************************;
%*                                                                   *;
%*  MACRO: DATATYP                                                   *;
%*                                                                   *;
%*  USAGE: %datatyp(parm)                                            *;
%*                                                                   *;
%*  DESCRIPTION:                                                     *;
%*    The DATATYP macro determines if the input parameter is         *;
%*    NUMERIC or CHARacter data, and returns either CHAR or NUMERIC  *;
%*    depending on the value passed in through parm.                 *;
%*                                                                   *;
%*  PROCEDURE:                                                       *;
%*    This macro checks first removes leading and trailing blanks.   *;
%*    Then it checks for and removes a sign character (+ or -).      *;
%*    Then it checks for a string of digits, a decimal point, or     *;
%*    a floating point exponent, followed by an optional sign and    *;
%*    a string of digits.                                            *;
%*                                                                   *;
%*  ERRORS/RESTRICTIONS:                                             *;
%*    This macro requires the %VERIFY macro.  Commas are not         *;
%*    allowed as the SAS word scanner does not accept them.  It      *;
%*    is not guaranteed that a string judged NUMERIC will in fact    *;
%*    be acceptable to the SAS word scanner as no range checking     *;
%*    is done.                                                       *;
%*                                                                   *;
%*********************************************************************;
   %local type char len pos fract expon;
   %let type=CHAR;
   %let parm=%qleft(%qtrim(&parm));
   %let len = %length(&parm);
   %if &len > 0 %then %do;
      %if %verify(&parm,%str(0123456789+-.EeDd))=0 %then %do;
         %let char = %qsubstr(&parm,1,1);
         %if &char=%str(+) | &char=%str(-) %then %do;
            %if &len < 2 %then %let parm = ;
            %else %let parm = %qsubstr(&parm,2);
            %let len = %eval(&len - 1);
            %end;
         %let fract = 0;
         %let expon = 0;
%repeat: %if &len > 0 %then %do;
            %let pos = %verify(&parm,0123456789);
            %if &pos = 0 %then %let type=NUMERIC;
            %else %do;
               %if &len > &pos %then %let parm = %qsubstr(&parm,&pos);
               %let len = %length(&parm);
               %let char = %qsubstr(&parm,1,1);
               %if &char=%str(.) & &fract=0 & &expon=0 %then %do;
                  %if &len < 2 %then %let parm = ;
                  %else %let parm = %qsubstr(&parm,2);
                  %let len = %eval(&len - 1);
                  %let fract = 1;
                  %goto repeat;
                  %end;
               %else %if (&char=%str(E) | &char=%str(e) |
                          &char=%str(D) | &char=%str(d)) &
                         &expon=0 & &pos>1 & &len>1 %then %do;
                  %let parm = %qsubstr(&parm,2);
                  %let len = %eval(&len - 1);
                  %let char = %qsubstr(&parm,1,1);
                  %if &char=%str(+) | &char=%str(-) %then %do;
                     %if &len < 2 %then %let parm = ;
                     %else %let parm = %qsubstr(&parm,2);
                     %let len = %eval(&len - 1);
                     %end;
                  %let expon = 1;
                  %goto repeat;
                  %end;
               %end;
            %end;
         %end;
      %end;
  &type
%mend datatyp;
by PROC Star
‎01-25-2017 07:59 PM - edited ‎01-25-2017 08:00 PM

> Currently the Boolean conversion-type does both character evaluations and numeric evaluations, without allowing me to specify my intent. 

 

Late to the game, but the guesswork can easily be avoided by quoting the tested values to force string comparisons

This makes the intent obvious.

 

%let a=5.1;
%let b=15e4;
%put 1-%sysevalf( &a > &b )/%sysevalf( &a < &b );
%put 2-%sysevalf("&a">"&b")/%sysevalf("&a"<"&b");

1-0/1
2-1/0

 

by PROC Star
on ‎01-25-2017 08:05 PM

This behaviour can even be managed though a resettable parameter.

%let a=5.1;
%let b=15e4;
%let q=;
%put 1-%sysevalf(&q&a&q>&q&b&q)/%sysevalf(&q&a&q<&q&b&q);
%let q=%str(%");
%put 2-%sysevalf(&q&a&q>&q&b&q)/%sysevalf(&q&a&q<&q&b&q);

1-0/1
2-1/0

 

 

 

Idea Statuses
Top Liked Authors