Sas 如何防止CATx函数对表达式求值

Sas 如何防止CATx函数对表达式求值,sas,sas-macro,Sas,Sas Macro,使用%sysfunc调用CATT()函数时,是否有方法阻止它对表达式求值 例如,给定代码: %let date=10-13-2015; %put %sysfunc(catt(The date Is:,&date)); 我希望它能返回: The date Is:10-13-2015 因为10-13-2015只是一个文本字符串。但是CATT()将连字符视为减法符号,并将其作为数值表达式进行计算,返回: The date Is:-2018 我尝试过宏引用,但没有改变任何东西,我想是因为我

使用%sysfunc调用CATT()函数时,是否有方法阻止它对表达式求值

例如,给定代码:

%let date=10-13-2015;
%put %sysfunc(catt(The date Is:,&date));
我希望它能返回:

The date Is:10-13-2015
因为10-13-2015只是一个文本字符串。但是CATT()将连字符视为减法符号,并将其作为数值表达式进行计算,返回:

The date Is:-2018
我尝试过宏引用,但没有改变任何东西,我想是因为我需要以某种方式对CATT()隐藏这些值。看起来,如果CATT的任何参数看起来像一个表达式,它将被视为表达式

另一个例子:

%let value=2 and 3;
%put %sysfunc(catt(The value Is:,&value));
The value Is:1
%let start_dt=10OCT2012 ;
%put %sysfunc(putn("&start_dt"d +1,date9));

如果可以这样做,只需删除逗号-无需将其分隔为单个参数(除非您使用的是
catx()
而不是
catt()

就我个人而言,我认为最好的方法是将日期存储为SAS日期值,然后使用
%sysfunc
的第二个(可选)参数应用格式。这提供了更好的灵活性

%let date = %sysfunc(mdy(10,13,2015));
%put The date Is: %sysfunc(sum(&date),mmddyyd10.);
如果您坚持原来的方法,并且使用的是
catx()
,那么我不知道该怎么做。我能得到的最接近的方法是插入一段文本,这样它就不能被解释为表达式,然后使用
tranwrd
删除该文本。漂亮、难看,并且留下了一个空间:

%let date=10-13-2015;
%let tmp=%sysfunc(catx(#, The date Is: , UNIQUE_STRING_TO_REMOVE&date ));
%let want=%sysfunc(tranwrd(&tmp, UNIQUE_STRING_TO_REMOVE, ));

%put &want;
给出:

The date Is:# 10-13-2015
我还尝试了宏引用的每一种组合,扫描了整个SAS函数列表,没有看到任何其他可行的选项。

对参数进行%SYSFUNC()求值的问题不限于CAT()一系列函数。任何接受数值的函数都会导致SAS尝试计算所提供的表达式

这可能是一个有用的功能。例如:

%let value=2 and 3;
%put %sysfunc(catt(The value Is:,&value));
The value Is:1
%let start_dt=10OCT2012 ;
%put %sysfunc(putn("&start_dt"d +1,date9));
您不需要使用CAT()函数来处理宏变量。只需展开彼此相邻的值,即可“连接”

如果您想制作一个类似CATX()函数的宏,那么这也不难做到

%macro catx /parmbuff ;
%local dlm return i ;
%if %length(&syspbuff) > 2 %then %do;
  %let syspbuff = %qsubstr(&syspbuff,2,%length(&syspbuff)-2);
  %let dlm=%qscan(&syspbuff,1,%str(,),q);
  %let return=%qscan(&syspbuff,2,%str(,),q);
  %do i=3 %to %sysfunc(countw(&syspbuff,%str(,),q));
    %let return=&return.&dlm.%qscan(&syspbuff,&i,%str(,),q);
  %end;
%end;
&return.
%mend catx;

%put %catx(|,a,b,c);
a|b|c
%put "%catx(",",a,b,c,d)";
"a","b","c","d"

不幸的是,我看不到一个简单的方法来解决这个问题。我确实看到理论上可以通过一个FCMP函数来传递它,尽管因为FCMP不允许真正的变量参数,这也不理想,但是

proc fcmp outlib=work.funcs.funcs;
  function catme(delim $, in_string $) $;
    length _result $1024;
    length _new_delim $1;
    _new_delim = scan(in_string,1,delim);
    do _i = 1 to countc(in_string,delim);
      _result = catx(_new_delim, _result, scan(in_string,_i+1,delim));
    end;
    return(_result);
  endfunc;
quit;

options cmplib=work.funcs;

%let date=10-13-2015;
%put %sysfunc(catme(|,:|The date Is| &date.));
或者在参数中添加引号,然后在CATx之后删除它们

%sysfunc(dequote(%sysfunc(catt(.... ,"&date."))))

乱七八糟。

没有
dosubl的函数式宏稍微有点疯狂:

%macro catx() /parmbuff;
%local rc dlm i params OUTSTR QWORD outstr;
%let SYSPBUFF = %qsubstr(&SYSPBUFF,2,%length(&SYSPBUFF)-2);
%let dlm = %qscan(&SYSPBUFF,1,%str(,));
%let params = %qsubstr(&SYSPBUFF,%index(&SYSPBUFF,%str(,))+1);

%let i = 1;
%let QWORD = %scan(&PARAMS,&i,%str(,));
%let OUTSTR = &QWORD;
%do %while(&QWORD ne);
    %let i = %eval(&i + 1);
    %let QWORD = %scan(&PARAMS,&i,%str(,));
    %if &QWORD ne %then %let OUTSTR = &OUTSTR.&DLM.&QWORD;
%end;

%unquote(&OUTSTR)
%mend catx;

%put %catx(%str( ),abc,10 - 1 + 2,def);
更疯狂但显然有效的选项-使用
%sysfunc(dosubl(…)
和大量宏逻辑来创建一个函数式宏,该宏以与
%sysfunc(catx(…)
相同的方式接受输入,但强制catx将所有输入作为文本处理,方法是引用它并在数据步骤中调用它

%macro catxt() /parmbuff;
%local rc dlm i params QPARAMS QWORD outstr;
%let SYSPBUFF = %qsubstr(&SYSPBUFF,2,%length(&SYSPBUFF)-2);
%let dlm = %qscan(&SYSPBUFF,1,%str(,));
%let params = %qsubstr(&SYSPBUFF,%index(&SYSPBUFF,%str(,))+1);

%let i = 1;
%let QWORD = "%scan(&PARAMS,&i,%str(,))";
%let QPARAMS = &QWORD;
%do %while(&QWORD ne "");
    %let i = %eval(&i + 1);
    %let QWORD = "%scan(&PARAMS,&i,%str(,))";
    %if &QWORD ne "" %then %let QPARAMS = &QPARAMS,&QWORD;
%end;

%let rc = %sysfunc(dosubl(%str(
    data _null_;
        call symput("OUTSTR",catx("&dlm",%unquote(&QPARAMS)));
    run;
)));

&OUTSTR
%mend catxt;

%put %catxt(%str( ),abc,10 - 1 + 2,def);

虽然这使用了一个数据步骤来执行
catx
dosubl
允许在任何可以正常使用
%sysfunc(catx(…)
)的地方运行整个程序。哇,好问题……这是一个困难的问题。明白了,@Tom,但这是一个简化的例子。在实际环境中,我希望使用catx()这是%SYSFUNC()的问题,不仅仅是CAT()函数的问题。我认为更多的是CAT()。也就是说,CAT()决定值是一个表达式,而对它进行求值。UPCASE()则不是。比较:%put%SYSFUNC(upcase(1或0));%put%sysfunc(catt(1或0));我认为是CAT的灵活性导致了宏设置中的这个问题。在数据步语言中,可以添加引号来表示它是字符串。谢谢@Robert。是的,您正确地猜到了我的真实代码是CATX()。我真正的代码不是日期(我同意这不是处理日期的方法,这似乎是最简单的示例)。我可能会采用您添加和删除字符串的方法,或者只是避免使用CATX()。但这是一个有趣的问题。我想这部分是因为CATX乐于处理数值参数的副作用。当然,这只是%SYSFUNC调用的问题,因为没有一个好的方式来表示“此值是字符串”。我喜欢这种解决方案。至少把混乱隐藏起来了。=)也许传递参数的方式可以改进?假设@Quentin需要一个
catx()
函数,将第一个参数保留为字符串用作分隔符,而只保留
%bquote
第二个参数(调用它时)这将允许您使用逗号分隔的值列表?我想如果
%bquote
像这样工作,它也适用于原始的
catx
,不幸的是。我想也许我解释得不够清楚。我想说的是,对
proc fcmp
参数进行一些细微的更改,您可能最终会得到一个调用非常类似于调用
%sysfunc(catx())
…能够这样调用它会很好:
%let want=%sysfunc(mycatx(myDelimiter,%bquote(param1,param2等,paramX));
谢谢@Joe,这是一个很好的提醒,提醒我以后需要学习FCMP。遗憾的是,
%put%sysfunc(dequote(%sysfunc(cat,日期:,“&date”)));
不起作用。它返回的
日期是:“10-13-2015”
我认为DEQUOTE()不能删除内部引号。但是@Robert's TRANWRD()解决方法与此类似。啊,一些DOSUBL宏函数神奇!漂亮的方法。由于一个奇怪的作用域冲突错误,我仍然在生产工作中远离DOSUBL。请注意,如果在宏调用之前在open code中添加
%let outsr=Hi Mom;
,宏将返回Hi Mom。即使您已正确声明不过,我认为DOSUBL对于这种方法有很大的希望,所以希望他们能很快修复这个错误。:)如果你想制作CATX()的宏版本,就不需要使用