如何重复调用SAS宏,其中第二个参数是第一个参数,带有-和。远离的?

如何重复调用SAS宏,其中第二个参数是第一个参数,带有-和。远离的?,sas,dynamic-variables,sas-macro,Sas,Dynamic Variables,Sas Macro,我有一个宏需要两个参数——一个可以包含破折号和句点的字符串,以及一个相同字符串的清理版本,其中任何破折号和句点都将替换为下划线 以下工作: %let foo=abc.def %mymacro(firstpar=&foo,secondpar=%sysfunc(translate(&foo,“\uuuuuuu”,“-”))) %let foo=abc def %mymacro(firstpar=&foo,secondpar=%sysfunc(translate(&foo,“\uuuuuuu”,“-”

我有一个宏需要两个参数——一个可以包含破折号和句点的字符串,以及一个相同字符串的清理版本,其中任何破折号和句点都将替换为下划线

以下工作:

%let foo=abc.def

%mymacro(firstpar=&foo,secondpar=%sysfunc(translate(&foo,“\uuuuuuu”,“-”)))

%let foo=abc def

%mymacro(firstpar=&foo,secondpar=%sysfunc(translate(&foo,“\uuuuuuu”,“-”)))

…但是我如何循环遍历一长串这样的变量呢?是否有一些标准的习惯用法,可以将列表中的元素一次分配给一个占位符变量,然后对命令求值,或者直接将它们替换到命令字符串中(但在我明确要求之前不对其求值)

我试着使用宏库,只要
&foo
不包含标点符号,它就工作得很好。如果他们这样做了,它就失败了,我尝试引用的任何组合都不能使它起作用

我的实际宏太长,太复杂,无法在这里发布。希望这个一般性问题足够清楚,可以回答:

“对于重复调用一次性调用类似于
%mymacro的宏(firstpar=&foo,secondpar=%sysfunc(translate(&foo,“\uuuuu”,“-”)));
,建议采用什么策略?
并且您有一个很大的
foo
值列表?”


或者换一种方式:“SAS中最接近R的
parse()
deparse()
功能的东西是什么?”

我无法理解R的parse和deparse,因为我不熟悉它们。但是,我只需传入第一个参数,并在宏中创建第二个宏变量。如下所示,FIRSTPAR与您在样本中的相同。我包括一个TRANSLATE参数,其中包含应替换的字符(默认为“.”):

替换句点和破折号:

%mymacro(firstpar=abc-de-fg.hi-.k,translate=.-)
替换句点、破折号和逗号:

%mymacro(firstpar=%str(abc-de,fg.hi,.k),translate=%str(.-,))
以下两个示例将SECONDPAR宏变量输出到日志:

abc_de_fg_hi__k

您在这里提到了“list”,因此方法将取决于您如何将该列表解析为宏。 例如,如果列表的格式已经可以直接用作宏变量

%let foo = abc-def abc-d-af abd-c-dgf; 
在这里,各个参数由空格分隔。宏变量保存所有变量。然后您可以逐个提取参数:

 %macro mymacro(firstpar = &foo, secondpar=%sysfunc(translate(&foo,"__","-.")));
   %do i = 1 %to %sysfunc(countw(&foo,' '));
          %let p1 = %scan(&foo, &i, ' ');
          %let p2 = %scan(&secondpar, &i, ' ');
          *<original macro statements>*
   %end;

现在列表已存储到
foo
,以空格分隔。然后可以将其解析为上面的宏。

我认为简单的问题是,如何从现有数据重复调用宏。在开始之前,我会说,通常情况下,您不会——您通常只是在普通SAS代码中执行任何操作。但是您没有给我们提供任何信息,所以您可能有一个很好的理由反复调用宏(我当然有时会这样做,尽管通常试图避免它)

我会在这个答案中加入上面DavB的答案——不要将两个值都作为参数,只使用第一个参数,然后在宏中将其转换为第二个参数

创建数据驱动的宏调用有十几种方法;这里有三个

%macro foo(param=);
%let second_param=%sysfunc(translate(&param,"___","-.a"));
%put &param &second_param;
%mend foo;


*Method 1: Call Execute.  This puts code in a queue to execute after the data step completes.  
    This has some advantages, namely that you can build code easily and can modify it with data step functions, 
    and some disadvantages, namely that some timing issues (when things happen internal to SAS) exist 
    and can be difficult to remember/work around.  Not used all that often as a result of those timing issues 
    but for simple things can be useful.;
data _null_;
set sashelp.class;
mcall = cats('%foo(param=',name,')');
call execute(mcall);
run;

*Method 2: PROC SQL select into.  This creates a list and stores it in a macro variable.  
    Quick and easy (much shorter code-wise) compared to the other solutions, but has one significant limitation:
    a hard limit to how many total characters can be stored in a macro variable (I forget exactly how much, but around 20,000).
    So if you are calling the macro a thousand or more times this might fail - it writes a warning to the log if so.
    Use NOPRINT with PROC SQL unless you want to see what is generated.;
proc sql;
select cats('%foo(param=',name,')') into :mcalllist separated by ' ' from sashelp.class;
quit;
&mcalllist;

*Method 3: Include file generation.  This creates an actual text file with your code in it, then you include that code.
    In some ways the most transparent - you can easily debug the code - but also a lot of lines of code to write comparatively.
    Does not have the length limitation of the PROC SQL method, although it does have the normal limitations of included code.
    The temp file is written to your WORK directory, so you can navigate to that to see the contents, and/or use a non-TEMP file
    and write it out to a directory of your choosing in order to see it.
;

filename foo temp;
data _null_;
file foo;
set sashelp.class;
mcall = cats('%foo(param=',name,')');
put mcall $;
run;
%include foo;

我不喜欢这个解决方案,因为它使用宏语言来完成SAS在数据步或其他方法方面已经做得更好的事情。如果您被迫使用宏语言(如果我想是为了家庭作业?),那么第一部分是可以接受的,但是将代码导入宏变量的步骤完全是无关的-如果您已经在使用select…into,然后在那里编写宏调用,而不是通过%do循环。这个解决方案只是为了满足OP的需要。OP没有指定列表的存储方式,所以我们只能猜测。第二部分只是一种可能性。我在这里遵循的原则是尽可能少地修改OP的原始宏,只关注变量操作。OP非常清楚地问,“重复调用宏的推荐策略是什么”。你上面所描述的并不是一个可以接受的方法。您的解决方案违反了SAS编程的一个基本原则—不要将数据存储在宏变量中。好的,我接受您的观点。实际上,当需要循环一行数据时,我经常使用IML。使用宏一次读取一行表,然后使用symget/symput也是一种方法。但是,我仍然认为,当数据量不是很大时,存储到宏变量并不像您所说的那样糟糕。您的解决方案(方法2)包括将事物选择到变量中。不是很相似吗?顺便说一下,我同意DavB的观点,即第二个参数应该在宏内部生成,而不是定义为参数。稍后我可能会更新答案。将代码(宏调用)存储在变量中很好-它存储的是我反对的实际数据。:)正如我所指出的,如果他实际上已经在一个宏变量中拥有所有数据,我不介意解决方案的第一部分——我不喜欢它,但这可能是正确的答案。第二部分是我反对的。对于这样的事情,IML当然是一个非常好的解决方案,尽管我通常不会用IML来回答,因为这是一个很多人都没有的昂贵的附加组件——我自己还在学习:)
 data tmp;
 input foo $;
 datalines;
 abc-def
 abcd-def
 abc-d-af
 ;
 run;

 proc sql noprint;
   select foo into :foo separated by ' ' from tmp;
 quit;
%macro foo(param=);
%let second_param=%sysfunc(translate(&param,"___","-.a"));
%put &param &second_param;
%mend foo;


*Method 1: Call Execute.  This puts code in a queue to execute after the data step completes.  
    This has some advantages, namely that you can build code easily and can modify it with data step functions, 
    and some disadvantages, namely that some timing issues (when things happen internal to SAS) exist 
    and can be difficult to remember/work around.  Not used all that often as a result of those timing issues 
    but for simple things can be useful.;
data _null_;
set sashelp.class;
mcall = cats('%foo(param=',name,')');
call execute(mcall);
run;

*Method 2: PROC SQL select into.  This creates a list and stores it in a macro variable.  
    Quick and easy (much shorter code-wise) compared to the other solutions, but has one significant limitation:
    a hard limit to how many total characters can be stored in a macro variable (I forget exactly how much, but around 20,000).
    So if you are calling the macro a thousand or more times this might fail - it writes a warning to the log if so.
    Use NOPRINT with PROC SQL unless you want to see what is generated.;
proc sql;
select cats('%foo(param=',name,')') into :mcalllist separated by ' ' from sashelp.class;
quit;
&mcalllist;

*Method 3: Include file generation.  This creates an actual text file with your code in it, then you include that code.
    In some ways the most transparent - you can easily debug the code - but also a lot of lines of code to write comparatively.
    Does not have the length limitation of the PROC SQL method, although it does have the normal limitations of included code.
    The temp file is written to your WORK directory, so you can navigate to that to see the contents, and/or use a non-TEMP file
    and write it out to a directory of your choosing in order to see it.
;

filename foo temp;
data _null_;
file foo;
set sashelp.class;
mcall = cats('%foo(param=',name,')');
put mcall $;
run;
%include foo;