call symput 究竟是如何工作的——尝试在 call symput 的帮助下创建一个迭代器
How exactly does call symput work - trying to create an iterator with help of call symput
我正在编写修改在上一个数据步骤中声明的数组的代码。因为它是一个新的数据步骤,所以旧索引将不起作用。我想我可以在 call symput 函数的帮助下使用迭代器。
我试图为每个 MID_(i) 数组元素分配 0 值,其中 month < "i" 所以我想出了代码:
data want;
set summary;
do i=1 to &MAX_MONTH.;
call symputx('iterator',i);
if MONTH < &iterator. then MID_&iterator. = 0;
end;run;
而且它不起作用。我正在试验调试它的代码并插入一个常量值而不是“i”:
data want;
set summary;
do i=1 to &MAX_MONTH.;
call symputx('iterator',7);
if MONTH < &iterator. then MID_&iterator. = 0;
end;run;
更让我困惑的是,这段代码只能使用一次。当我将“7”更改为其他数字时,结果保持不变,直到我重置 SAS,之后它将使用更改后的值,但仍然 - 只有一次。
这里发生了什么?我不明白什么?如何创建工作迭代器?
symput
和 symputx
在数据步骤结束后创建宏变量。不能在同一个数据步骤中访问正在创建的宏变量。每次调用symput
,更新最后输出的宏变量
根据 call symput
documentation:
You cannot use a macro variable reference to retrieve the value of a
macro variable in the same program (or step) in which SYMPUT creates
that macro variable and assigns it a value.
You must specify a step
boundary statement to force the DATA step to execute before
referencing a value in a global statement following the program (for
example, a TITLE statement). The boundary could be a RUN statement or
another DATA or PROC statement.
您不需要使用 symput
来实现您的目标。 i
已经迭代,如果您创建一个新的 mid_
变量数组,您可以使用它。
data want;
set summary;
array mid_[&MAX_MONTH.];
do i=1 to dim(mid_);
if MONTH < i then MID_[i] = 0;
end;
run;
我建议完全坚持使用数组,如果您的变量有命名约定,则不需要其他任何东西。
我没有你的数据,但我想知道这样的简化是否也可行。
data want;
set summary;
array mid_[*] mid_:;
do i=1 to month-1;
MID_[i] = 0;
end;
run;
宏处理器首先将宏表达式转换为文本。所以 &MAX_MONTH
和 &iterator
在 SAS 甚至开始编译数据步骤之前就已经被它们的值替换了,而且肯定在它有机会 运行 CALL SYMPUTX() 或IF 语句。
所以如果 MAX_MONTH 的值为 12 而 ITERATOR 的值为 7 那么你 运行 这个数据步骤:
data want;
set summary;
do i=1 to 12;
call symputx('iterator',i);
if MONTH < 7 then MID_7 = 0;
end;
run;
这与 运行ning:
相同
data want;
set summary;
if MONTH < 7 then MID_7 = 0;
i=13;
run;
%let iterator=12;
ARRAY 语句是数据步骤方法,用于按变量在列表中的位置引用变量。所以如果你想引用名称如 MID_1、MID_2 等的变量,那么定义一个数组并使用数组的索引。您仍然可以使用 MAX_MONTH 宏变量来定义要包含在数组中的变量集。
所以也许你是想 运行 这样的事情:
data want;
set summary;
array mid_ [&max_month] ;
do index=month+1 to dim(mid_);
MID_[index] = 0;
end;
drop index;
run;
Stu、Tom 和 Reeza 都用你应该做的方式回答了这个问题。
但是,为了完整起见,以下是使用宏变量执行此操作的方法:也可以使用宏 %do
。这不是解决您的确切问题的正确方法,但有些问题可能需要这种方法。
%let max_month=12;
data summary;
do month = 1 to 12;
output;
end;
run;
%macro do_months(max_month);
data want;
set summary;
%do i=1 %to &MAX_MONTH.;
if MONTH < &i. then MID_&i. = 0;
%end;
run;
%mend do_months;
%do_months(max_month=12);
在这里,如果您打开 options mprint;
,您可以看到 SAS 在做什么:它正在为您创建 12 个 if
语句,每个语句对于迭代器和 mid_ 变量都有不同的值.每次通过数据步骤执行所有 12 个。它的效率不如数组解决方案,而且更难调试,所以除非你需要,否则不要这样做。
Tom 就如何解决您的问题给出了很好的建议。然而,没有人解释调用 symputx() 是如何工作的:Stu Sztukowski 的回答部分不正确,因为它以错误的方式解释了 SAS 文档。
有两种语言:SAS Base(data step、proc sql等)和SAS Macro(%let、%put、&var等)。所以有两个世界:SAS Base 世界和 SAS Macro 世界。您可以使用特定功能在这两个世界之间交换数据。
1.从 SAS 宏访问 SAS Base 世界。
您可以使用特定宏 %sysfunc()
执行大多数 SAS Base 函数。示例:
%let mvSrc = there are multiple blanks;
%let mvVar = %qsysfunc(compbl(%nrbquote(&mvSrc)));
%put &=mvVar; /* prints MVVAR=there are multiple blanks */
最好在宏变量前加上 mv 前缀。
我还使用宏 %nrbquote()
来处理具有特定于 SAS 宏符号的字符串,例如方括号、逗号、引号、&符号等。这里不需要,但这也是一个很好的做法。
2。从 SAS 基本代码访问 SAS 宏世界。
data _null_;
call symputx('mvVar', 'hey!', 'g'); /* 1 */
var = symget('mvVar'); /* 2 */
put var=; /* 3 */
call symputx('mvVar', 'whats' up?', 'g');
var = symget('mvVar');
put var=;
run;
输出结果如下:
hey!
what's up?
工作原理:
- 在全局范围内创建值为 hey! 的宏变量
mvVar
。
- 将最接近的(本地、调用宏之一、...、全局)宏变量
mvVar
读入数据步变量 var
。
- 打印值。
应该使用那些功能吗?至于调用 symputx() 答案是肯定的,您必须使用它来将数据放入宏观世界。至于 symget(),答案是有时你应该使用它,有时用简单的宏变量替换更容易,即 var = "&mvVar";
。替换的问题是当宏变量包含双引号时它不会工作。例如,下面的代码
%let mvVar = hey, "Mr X"!;
data _null_;
var = "&mvVar";
run;
变成错误代码:
data _null_;
var = "hey, "Mr X"!"; /* error! */
run;
还要记住,替换只发生一次,在数据步骤被编译之前,因此在它被执行之前。以下代码
%let mvVar = hello;
data _null_;
var = "&mvVar";
put var=;
call symputx('mvVar', 'yes?', 'g');
var = "&mvVar";
put var=;
run;
变成
data _null_;
var = "hello";
put var=;
call symputx('mvVar', 'yes?', 'g');
var = "hello";
put var=;
run;
并打印
hello
hello
SAS 还包括一种在数据步内执行宏代码的机制。该机制使用 dosubl()
函数或 call execute()
例程。该机制是一个鸭带,很难理解,它以非直观的方式工作,绝对不能使用 。绝对不会。曾经.
我正在编写修改在上一个数据步骤中声明的数组的代码。因为它是一个新的数据步骤,所以旧索引将不起作用。我想我可以在 call symput 函数的帮助下使用迭代器。 我试图为每个 MID_(i) 数组元素分配 0 值,其中 month < "i" 所以我想出了代码:
data want;
set summary;
do i=1 to &MAX_MONTH.;
call symputx('iterator',i);
if MONTH < &iterator. then MID_&iterator. = 0;
end;run;
而且它不起作用。我正在试验调试它的代码并插入一个常量值而不是“i”:
data want;
set summary;
do i=1 to &MAX_MONTH.;
call symputx('iterator',7);
if MONTH < &iterator. then MID_&iterator. = 0;
end;run;
更让我困惑的是,这段代码只能使用一次。当我将“7”更改为其他数字时,结果保持不变,直到我重置 SAS,之后它将使用更改后的值,但仍然 - 只有一次。
这里发生了什么?我不明白什么?如何创建工作迭代器?
symput
和 symputx
在数据步骤结束后创建宏变量。不能在同一个数据步骤中访问正在创建的宏变量。每次调用symput
,更新最后输出的宏变量
根据 call symput
documentation:
You cannot use a macro variable reference to retrieve the value of a macro variable in the same program (or step) in which SYMPUT creates that macro variable and assigns it a value.
You must specify a step boundary statement to force the DATA step to execute before referencing a value in a global statement following the program (for example, a TITLE statement). The boundary could be a RUN statement or another DATA or PROC statement.
您不需要使用 symput
来实现您的目标。 i
已经迭代,如果您创建一个新的 mid_
变量数组,您可以使用它。
data want;
set summary;
array mid_[&MAX_MONTH.];
do i=1 to dim(mid_);
if MONTH < i then MID_[i] = 0;
end;
run;
我建议完全坚持使用数组,如果您的变量有命名约定,则不需要其他任何东西。
我没有你的数据,但我想知道这样的简化是否也可行。
data want;
set summary;
array mid_[*] mid_:;
do i=1 to month-1;
MID_[i] = 0;
end;
run;
宏处理器首先将宏表达式转换为文本。所以 &MAX_MONTH
和 &iterator
在 SAS 甚至开始编译数据步骤之前就已经被它们的值替换了,而且肯定在它有机会 运行 CALL SYMPUTX() 或IF 语句。
所以如果 MAX_MONTH 的值为 12 而 ITERATOR 的值为 7 那么你 运行 这个数据步骤:
data want;
set summary;
do i=1 to 12;
call symputx('iterator',i);
if MONTH < 7 then MID_7 = 0;
end;
run;
这与 运行ning:
相同data want;
set summary;
if MONTH < 7 then MID_7 = 0;
i=13;
run;
%let iterator=12;
ARRAY 语句是数据步骤方法,用于按变量在列表中的位置引用变量。所以如果你想引用名称如 MID_1、MID_2 等的变量,那么定义一个数组并使用数组的索引。您仍然可以使用 MAX_MONTH 宏变量来定义要包含在数组中的变量集。
所以也许你是想 运行 这样的事情:
data want;
set summary;
array mid_ [&max_month] ;
do index=month+1 to dim(mid_);
MID_[index] = 0;
end;
drop index;
run;
Stu、Tom 和 Reeza 都用你应该做的方式回答了这个问题。
但是,为了完整起见,以下是使用宏变量执行此操作的方法:也可以使用宏 %do
。这不是解决您的确切问题的正确方法,但有些问题可能需要这种方法。
%let max_month=12;
data summary;
do month = 1 to 12;
output;
end;
run;
%macro do_months(max_month);
data want;
set summary;
%do i=1 %to &MAX_MONTH.;
if MONTH < &i. then MID_&i. = 0;
%end;
run;
%mend do_months;
%do_months(max_month=12);
在这里,如果您打开 options mprint;
,您可以看到 SAS 在做什么:它正在为您创建 12 个 if
语句,每个语句对于迭代器和 mid_ 变量都有不同的值.每次通过数据步骤执行所有 12 个。它的效率不如数组解决方案,而且更难调试,所以除非你需要,否则不要这样做。
Tom 就如何解决您的问题给出了很好的建议。然而,没有人解释调用 symputx() 是如何工作的:Stu Sztukowski 的回答部分不正确,因为它以错误的方式解释了 SAS 文档。
有两种语言:SAS Base(data step、proc sql等)和SAS Macro(%let、%put、&var等)。所以有两个世界:SAS Base 世界和 SAS Macro 世界。您可以使用特定功能在这两个世界之间交换数据。
1.从 SAS 宏访问 SAS Base 世界。
您可以使用特定宏 %sysfunc()
执行大多数 SAS Base 函数。示例:
%let mvSrc = there are multiple blanks;
%let mvVar = %qsysfunc(compbl(%nrbquote(&mvSrc)));
%put &=mvVar; /* prints MVVAR=there are multiple blanks */
最好在宏变量前加上 mv 前缀。
我还使用宏 %nrbquote()
来处理具有特定于 SAS 宏符号的字符串,例如方括号、逗号、引号、&符号等。这里不需要,但这也是一个很好的做法。
2。从 SAS 基本代码访问 SAS 宏世界。
data _null_;
call symputx('mvVar', 'hey!', 'g'); /* 1 */
var = symget('mvVar'); /* 2 */
put var=; /* 3 */
call symputx('mvVar', 'whats' up?', 'g');
var = symget('mvVar');
put var=;
run;
输出结果如下:
hey!
what's up?
工作原理:
- 在全局范围内创建值为 hey! 的宏变量
mvVar
。 - 将最接近的(本地、调用宏之一、...、全局)宏变量
mvVar
读入数据步变量var
。 - 打印值。
应该使用那些功能吗?至于调用 symputx() 答案是肯定的,您必须使用它来将数据放入宏观世界。至于 symget(),答案是有时你应该使用它,有时用简单的宏变量替换更容易,即 var = "&mvVar";
。替换的问题是当宏变量包含双引号时它不会工作。例如,下面的代码
%let mvVar = hey, "Mr X"!;
data _null_;
var = "&mvVar";
run;
变成错误代码:
data _null_;
var = "hey, "Mr X"!"; /* error! */
run;
还要记住,替换只发生一次,在数据步骤被编译之前,因此在它被执行之前。以下代码
%let mvVar = hello;
data _null_;
var = "&mvVar";
put var=;
call symputx('mvVar', 'yes?', 'g');
var = "&mvVar";
put var=;
run;
变成
data _null_;
var = "hello";
put var=;
call symputx('mvVar', 'yes?', 'g');
var = "hello";
put var=;
run;
并打印
hello
hello
SAS 还包括一种在数据步内执行宏代码的机制。该机制使用 dosubl()
函数或 call execute()
例程。该机制是一个鸭带,很难理解,它以非直观的方式工作,绝对不能使用 。绝对不会。曾经.