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,之后它将使用更改后的值,但仍然 - 只有一次。

这里发生了什么?我不明白什么?如何创建工作迭代器?

symputsymputx 在数据步骤结束后创建宏变量。不能在同一个数据步骤中访问正在创建的宏变量。每次调用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?

工作原理:

  1. 在全局范围内创建值为 hey! 的宏变量 mvVar
  2. 将最接近的(本地、调用宏之一、...、全局)宏变量 mvVar 读入数据步变量 var
  3. 打印值。

应该使用那些功能吗?至于调用 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() 例程。该机制是一个鸭带,很难理解,它以非直观的方式工作,绝对不能使用 。绝对不会。曾经.