【问题标题】:Macro variable has different value within DATA Step. Why?宏变量在 DATA Step 中具有不同的值。为什么?
【发布时间】:2016-10-05 00:17:34
【问题描述】:

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

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

请考虑,

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_var 采用dataset_var1 的值,即value1value3,而不是像您期望的那样保留名称dataset_var1。一旦超出 DATA 步,macro_var 的值就会神奇地返回到正确的值。

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

%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,但不确定在哪里可以找到这种行为的解释。

【问题讨论】:

    标签: sas sas-macro


    【解决方案1】:

    简短回答: 尝试使用宏 %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 等之间的区别是学习使用宏语言的关键步骤。

    【讨论】:

    • 谢谢@Joe,我稍微修改了那个总结声明。
    【解决方案2】:

    宏变量取 value1 和 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 没有改变。

    宏变量的行为似乎与一切背道而驰 我曾经从 BASIC、C++、Java、 C#、VBA、Python、Lisp 和 R。

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

    【讨论】:

    • 在最后半句之前,您的回答非常好......当它非常有价值时,令人困惑的事实并不是放弃它的理由。没有 任何 宏变量或宏的 SAS 编程会非常痛苦......
    • 是的,在某些情况下避免使用 SAS 宏可能会很痛苦、很困难并且不可能。尽管如此,它还是一种无类型的动态语言,再加上您经常在 SAS 商店中发现缺乏正式的编程纪律,它是一个肥沃的错误花园。
    【解决方案3】:

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

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

    %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 在每个步骤中将变量解析为什么以及逻辑是如何评估的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-09-10
      相关资源
      最近更新 更多