我正在尝试做一个以其他宏为输入的宏,这些宏可以有不同数量的参数。想象一下我有一个宏:
%MACRO foo(text = );
%PUT &text.;
%MEND foo;
我想创建一个call_macro
宏,它调用foo()
,指定文本。问题是,我可能想用其他参数调用另一个宏fuu()
。
我想到过这样的东西:
%MACRO call_macro(macro = , args = );
%¯o.(&args.);
%MEND call_macro;
%call_macro(macro=foo, args='text=Hello');
这显然不起作用,因为我调用的是%foo('text=Hello')
而不是%foo(text=Hello)
。有办法摆脱这种局面吗?
(就上下文而言,我正试图为SAS创建一个在R中使用的映射函数。目标是有一个函数map
,它接受数据集列表、要应用的函数和该函数的参数,在所有数据集上执行该函数(
如果要在SAS中使用DATA,请使用SAS数据步骤和过程。即使数据实际上是关于要运行的代码的元数据,也是如此。
因此,如果你有一个包含三个变量的数据集,REMOTE、MACRO和ARGS,你可以使用它来生成一系列RSUBMIT/ENDRSUBMIT块,将宏调用发送到每个不同的并行进程。然后只需使用%INCLUDE来运行生成的代码。
filename code temp;
data _null_;
set metadata ;
by remote ;
file code;
if first.remote then put 'rsubmit ' remote ';' ;
put '%' macro '(' args ');';
if last.remote then put 'endrsubmit;' ;
run;
%include code / source2;
事实上,RSUBMIT语句可能需要更复杂才能真正实现并行执行,但如何从元数据生成代码的想法才是关键。
只需省略引号。
%MACRO call_macro(macro = , args = );
%¯o.(&args.);
%MEND call_macro;
%call_macro(macro=foo, args=text=Hello);
如果在没有等号的情况下调用宏,它将不起作用因为在你的通话中
%call_macro(foo, text=Hello);
第二自变量将不被解释为具有值text=Hello
而是被解释为值Hello
的参数text
。
如果使用PARMBUFF
选项对调度宏进行编码,则不需要将参数嵌入到另一个参数中。PARMBUFF为宏提供参数,包括任何边界圆括号。
示例:
将调度的宏参数作为调度宏的一阶参数提供。
%macro one (data=);
%put INFO: Macro:&SYSMACRONAME. &=data;
%mend;
%macro two (data=, vars=);
%put INFO: Macro:&SYSMACRONAME. &=data &=vars;
%mend;
%macro three (data=, vars=, out=);
%put INFO: Macro:&SYSMACRONAME. &=data &=vars &=out;
%mend;
%macro dispatch (macro=) / PARMBUFF;
%put INFO: -----;
%put INFO: &SYSMACRONAME. &=SYSPBUFF;
%put INFO: &=macro;
%if %length(¯o) %then %do;
%let syspbuff = %sysfunc(prxchange(%str(s/,?s*macros*=s*w+,?//i),-1,&syspbuff));
%local invoke;
%let invoke = ¯o &syspbuff;
%&invoke
%end;
%put INFO: -----;
%mend;
%dispatch(macro=one, data=foobar)
%dispatch(data=foobar, macro = one)
%dispatch(macro=two, data=foobar, vars=p q r)
%dispatch(macro=three, data=foobar, vars=p q r, out=snafu)
%one (data=x)
将记录
INFO: -----
INFO: DISPATCH SYSPBUFF=(macro=one, data=foobar)
INFO: MACRO=one
INFO: Macro:ONE DATA=foobar
INFO:
INFO: -----
INFO: DISPATCH SYSPBUFF=(data=foobar, macro = one)
INFO: MACRO=one
INFO: Macro:ONE DATA=foobar
INFO:
INFO: -----
INFO: DISPATCH SYSPBUFF=(macro=two, data=foobar, vars=p q r)
INFO: MACRO=two
INFO: Macro:TWO DATA=foobar VARS=p q r
INFO:
INFO: -----
INFO: DISPATCH SYSPBUFF=(macro=three, data=foobar, vars=p q r, out=snafu)
INFO: MACRO=three
INFO: Macro:THREE DATA=foobar VARS=p q r OUT=snafu
INFO:
INFO: Macro:ONE DATA=x
如果您确实想这样做,那么您需要保护子宏参数中的逗号。您可以尝试宏引用或实际引用。但最简单的方法是利用这样一个事实,即宏调用中括号内的逗号不会被视为指示新参数的开始。
因此,只需将不使用%
作为单个参数的宏调用传递给call_macro宏即可。
%macro call_macro(arg);
%put &=sysmacroname: &=arg ;
%&arg.
%mend;
%macro test1(a,b=);
%put &=sysmacroname: &=a &=b ;
%mend;
%macro test2(c,d,e);
%put &=sysmacroname: &=c &=d &=e;
%mend;
%call_macro(test1(b=xyz,a=123))
%call_macro(test2(456,e=abc,d=89))
%call_macro(foo(text=Hello))
如果由于某种原因CALL_MACRO需要知道它要调用的宏的名称,可以通过简单的%SCAN((函数调用来提取。
%local macroname;
%let macroname=%scan(&arg,1,());
我解决了。设置args="par_1=val_1, par_2=val_2"
,然后在宏内部使用args=%sysfunc(COMPRESS(&args.,'"');