宏变量在 DATA 步中具有不同的值。为什么?

Macro variable has different value within DATA Step. Why?

我有理由使用重复过程处理给定数据集的不同变量。为了解决这个问题,我编写了一个宏,其输入是感兴趣的特定变量。然后宏将只处理该变量。然而,事实证明其中一个变量需要稍微不同地处理。我的快速修复是应用条件;如果变量是异常,则执行与其他变量不同的操作。问题解决了吧?没有。

我发现宏变量的值会根据它是否在数据步骤中使用而变化。

请考虑,

data example;
  length dataset_var1 $ 6 dataset_var2 $ 6;
  input dataset_var1 $ dataset_var2;
  datalines;
  value1 value2
  value3 value4
  ;
run;

宏及其调用:

%macro NoQuotes(macro_var);
  %put &macro_var. ;

  data _null;
    set example;

    put &macro_var. ;

    if &macro_var. = 'dataset_var1' then do; 
        put "The IF evaluated";
      end;
    else do;
        put "The ELSE evaluated";
      end;
  run;

  %put &macro_var. ;
%mend;

%NoQuotes(dataset_var1);

这会产生以下日志条目:

dataset_var1

value1
The ELSE evaluated
value3
The ELSE evaluated
NOTE: There were 2 observations read from the data set WORK.EXAMPLE.
NOTE: The data set WORK._NULL has 2 observations and 2 variables.
NOTE: DATA statement used (Total process time):
      real time           0.01 seconds
      cpu time            0.00 seconds


dataset_var1

注意 macro_var 的值如何根据它是否在 DATA 步内而变化。在 DATA 步中,macro_vardataset_var1 的值,即 value1value3,而不是像您期望的那样保留名称 dataset_var1。一旦在 DATA 步之外,macro_var 的值神奇地 returns 到它的正确值。

在同事的建议下,我将宏变量的名称放在条件语句中的引号中。这使得条件行为符合预期。

%macro WithQuotes(macro_var);
  %put &macro_var. ;

  data _null_;
    set example;

    put &macro_var. ;

    if "&macro_var." = 'dataset_var1' then do; 
        put "The IF evaluated";
      end;
    else do;
        put "The ELSE evaluated";
      end;
  run;

  %put &macro_var. ;
%mend;

%WithQuotes(dataset_var1);

这会产生以下日志条目:

dataset_var1

value1
The IF evaluated
value3
The IF evaluated
NOTE: There were 2 observations read from the data set WORK.EXAMPLE.
NOTE: DATA statement used (Total process time):
      real time           0.00 seconds
      cpu time            0.00 seconds


dataset_var1

虽然条件现在按预期执行,但我们再次看到宏变量采用 value1value3 的值。

宏变量的行为似乎 运行 与我从 BASIC、C++、Java、C#、VBA、Python、Lisp 和 R.

有人可以向我解释一下这是怎么回事吗?我已经阅读了大部分 Macro Language Reference,但不确定在哪里可以找到对这种行为的解释。

宏语言生成代码。简单来说,它会进行文本替换。

因此您的第一个宏调用会创建以下代码

%put dataset_var1 ;

data _null;
set example;

put dataset_var1 ;

if dataset_var1 = 'dataset_var1' then do; 
    put "The IF evaluated";
  end;
else do;
    put "The ELSE evaluated";
  end;
run;

%put dataset_var1 ;

因为变量存在于数据集上,所以它引用了变量值,而您看到的输出正是我所期望的。

添加引号时,您不再引用变量,而是创建一个与 IF 条件中的值匹配的文本字符串。

这是字符串与字符串的比较:

 if "dataset_var1" = 'dataset_var1' then do;

这引用了一个变量 dataset_var1,它包含两个值,value1/value3。

if dataset_var1 = 'dataset_var1' then do; 

如果您使用 OPTIONS MPRINT SYMBOLGEN;,您将看到 SAS 在每个步骤中将变量解析为什么以及逻辑是如何计算的。

the macro variable takes on the values of value1 and value3

否:宏变量是一串字符dataset_var1。宏变量不变。宏变量与数据步骤中同名的变量不同。每次 SAS 宏处理器看到 &dataset_var1 时,它都会用字符串 dataset_var1 替换它。 SAS 宏处理器通过字符串替换生成代码,而不是计算代码。

提示:如果您打开 options mprint;,您可以看到宏处理器生成的代码。

%put &macro_var;&macro_var 替换为字符串 dataset_var1 并将字符串 dataset_var1 放入日志中,就像您写了 %put dataset_var1;

在数据步骤中,宏处理器重写了以下语句:

put &macro_var;变成 put dataset_var1;

put(不是%put),将数据集变量的值写入日志,所以这把变量的值dataset_var1 到日志。此变量是 'value1' 或 'value3'.

if "&macro_var." = 'dataset_var1' 变成 if dataset_var1 = 'dataset_var1' 它不是比较 &macro_var 和 'dataset_var1`,而是比较 'value1' = 'dataset_var1'第一行,'value3' = 'dataset_var1'。这对两行都是错误的。

最后的 %put &macro_var; 与第一个相同,因为 &macro_var 没有改变。

The behavior of macro variables seems to run contrary to everything I've ever known about the concept of variables from BASIC, C++, Java, C#, VBA, Python, Lisp, and R.

那是因为SAS宏变量不是SAS变量! SAS 宏更像是 C pre-processor statements,而不是 C 代码。 SAS 宏变量就像编译器 #define 指令。根据我的经验,它们是 SAS 程序中最常见的混乱和错误原因之一,应该尽可能消除!

简答: 尝试使用宏 %IF 语句而不是数据步语言 IF 语句。

长答案: 宏语言是一种预处理器,它处理文本以生成代码。一般来说,宏语言并不知道 SAS 数据集或数据集中的变量。 DATA 步语言处理数据集和数据集变量。宏语言看起来和DATA步语言很像,但是它们的用途完全不同,是两种不同的语言。

考虑:

87   options mprint;
88
89   %macro ShowValue(var);
90     data _null_;
91       set sashelp.class (obs=3);
92
93       %put The macro variable VAR has the value: &VAR;
94       put "The dataset variable &VAR has the value: " &VAR;
95
96     run;
97
98   %mend;
99
100  %ShowValue(var=height)
MPRINT(SHOWVALUE):   data _null_;
MPRINT(SHOWVALUE):   set sashelp.class (obs=3);
The macro variable VAR has the value: height
MPRINT(SHOWVALUE):   put "The dataset variable height has the value: " height;
MPRINT(SHOWVALUE):   run;

The dataset variable height has the value: 69
The dataset variable height has the value: 56.5
The dataset variable height has the value: 65.3
NOTE: There were 3 observations read from the data set SASHELP.CLASS.

宏语言有一个 %PUT 语句,数据步语言有一个 PUT 语句。上面,%PUT 语句的目的是显示名为 VAR 的宏变量(参数)的值。用户已将值 height 传递给它。上面 PUT 语句的目的是显示用户命名的数据步骤变量的值。因为用户已经传递了值 height,所以列出了该数据集变量的值(针对处理的所有三个记录)。名为 VAR 的宏变量始终具有值 height。名为 height 的数据集变量对于不同的记录具有不同的值。请注意,%PUT 语句仅执行一次,即使它在数据步循环内也是如此。这是因为在执行(甚至编译)任何数据步骤代码之前执行宏语句。 PUT 语句是数据步骤代码,因此它为数据步骤处理的每条记录编译和执行一次。 PUT 语句需要引号,以便它可以区分文本文字和数据步骤变量的名称。 %PUT 语句(实际上整个宏语言)不需要引号,因为宏变量由 &.

引用

如果要根据宏变量的值进行条件求值来决定宏应该生成哪个数据步,可以使用宏%IF语句,例如:

102  %macro ShowValue(var);
103    data _null_;
104      set sashelp.class (obs=3);
105
106      %if &var=height %then %do;
107        &var=&var * 2.54; *convert height from inches to cm;
108      %end;
109
110      %put The macro variable VAR has the value: &VAR;
111      put "The dataset variable &VAR has the value: " &VAR;
112
113    run;
114
115  %mend;
116
117  %ShowValue(var=height)
MPRINT(SHOWVALUE):   data _null_;
MPRINT(SHOWVALUE):   set sashelp.class (obs=3);
MPRINT(SHOWVALUE):   height=height * 2.54;
MPRINT(SHOWVALUE):   *convert height from inches to cm;
The macro variable VAR has the value: height
MPRINT(SHOWVALUE):   put "The dataset variable height has the value: " height;
MPRINT(SHOWVALUE):   run;

The dataset variable height has the value: 175.26
The dataset variable height has the value: 143.51
The dataset variable height has the value: 165.862
NOTE: There were 3 observations read from the data set SASHELP.CLASS.

118  %ShowValue(var=weight)
MPRINT(SHOWVALUE):   data _null_;
MPRINT(SHOWVALUE):   set sashelp.class (obs=3);
The macro variable VAR has the value: weight
MPRINT(SHOWVALUE):   put "The dataset variable weight has the value: " weight;
MPRINT(SHOWVALUE):   run;

The dataset variable weight has the value: 112.5
The dataset variable weight has the value: 84
The dataset variable weight has the value: 98
NOTE: There were 3 observations read from the data set SASHELP.CLASS.

宏 %IF 语句解析宏变量并将该宏变量的解析值与文本字符串进行比较 height。请注意,%IF 语句中不需要(或不需要)引号。因为宏语言是处理文本的,所以不使用引号来表示文本值。

简而言之,使用宏%IF语句根据宏变量中存储的文本值进行决策,以控制宏语言生成哪些数据步代码,并使用数据步IF语句进行决策数据步变量的值来控制执行哪个数据步代码。 (IF语句中引用的数据步变量的名称可能是使用宏变量生成的,甚至是宏。)

了解 %PUT 与 PUT、%IF 与 IF、%DO 与 DO 等之间的区别是学习使用宏语言的关键步骤。