SAS:从Data步骤循环中调用宏

时间:2016-11-11 17:16:03

标签: macros sas abstraction

为了重构一个程序,我采用了一个复杂的过程,我想要抽象并将它放在一个宏中。

%macro BlackBox();
  data _null_;
    put "This represents a complex process I want to abstract.";
  run;
%mend;

该过程需要连续多次发生,因此显而易见的解决方案是将其置于循环中。

data _null_;
  do i = 1 to 3;
    %BlackBox();
  end;
run;

然而,这会产生以下错误。

ERROR 117-185: There was 1 unclosed DO block.

发生了什么事?

我最好的猜测是SAS正在尝试在数据步骤中运行数据步骤。

我发现我可以通过将循环包含在宏中然后立即调用宏来避免此错误。

%macro PerformDoLoop();
  %do i = 1 %to 3;
    %BlackBox();
  %end;
%mend;
%PerformDoLoop;

所有这些似乎都是处理基本编程任务的迂回方式。我希望更多地了解数据步骤方法失败的原因,这将让我深入了解如何更优雅地完成此任务。

请理解这是一个简化的示例,用于说明我遇到的错误。宏的实例可以包含参数或返回值。

2 个答案:

答案 0 :(得分:2)

你的假设是完全正确的; SAS正试图在数据步骤中执行数据步骤,当然不会去任何地方(好吧,它可能,但只是......复杂)。

宏循环方法是完全合理的,我认为其他编程语言基本上就是你要做的。您编写方法draw_box以在C#中在屏幕上显示一个框,然后您编写一个方法draw_three_boxes,通过调用draw_box三次在屏幕上显示三个框。

现在,它看起来很愚蠢的原因是你的编程设计不好,因为draw_three_boxes方法非常有限:它只能做一件事,画三个盒子,所以你为什么不做最初的draw_box方法首先做到了吗?

据推测,你应该做的是写draw_box,然后写draw_boxes(int count, int xpos, int ypos)或类似的东西,对吧?这里也是一样的。你不应该像你那样编写PerformDoLoop()宏,因为你正在硬编码执行循环的次数。

而是说明为什么你要运行它三次。如果你真的知道并且不是一段数据,那就写下%PerformDoLoop(count=),然后拨打%PerformDoLoop(count=3)。或者在原始宏中包含%do循环,使用count参数,默认为1。

更有可能的是,数据驱动的原因是这样做了3次。你有3个州。你有3个班级的学生。随你。用它来生成对%BlackBox的调用。这将给你最好的结果,因为那时你没有在程序中做 - 你的数据改变,你立即得到2或4或任何电话。

您可以在SESUG 2016上看到我最近发表的论文Writing Code With Your Data,了解有关如何执行此操作的更多信息。

答案 1 :(得分:1)

宏语言是预处理器。它生成SAS代码,并在DATA步骤代码编译之前执行。使用您的代码:

data _null_;
  do i = 1 to 3;
    %BlackBox();
  end;
run;

宏%BlackBox()将执行一次(不是三次,因为它在DO循环执行之前执行,概念上在DO循环之外)。数据步骤代码变为:

data _null_;
  do i = 1 to 3;
    data _null_;
    put "This represents a complex process I want to abstract.";
    run;
  end;
run;

正如您所说,SAS无法在另一个数据步骤中执行数据步骤。第3行的data _null_结束第一个数据步骤,将其留在未闭合的do块中。

我同意@ Joe的观点。如果要生成许多宏调用,使用宏%DO循环来执行此操作是一种很好的方法。他的论文通过构建解析为宏调用列表的宏变量,为使用数据生成宏调用提供了一个很好的方法。

另一种有用的学习方法是CALL EXECUTE。这允许您使用数据步骤来生成宏调用。 CALL EXECUTE在执行数据步骤时生成宏调用,宏将在数据步骤之外执行(当您使用%NRSTR时,如下所示)。例如:

data _null_;
  do i = 1 to 3;
    call execute ('%nrstr(%BlackBox())');
  end;
run;

将生成三个宏调用:

NOTE: CALL EXECUTE generated line.
1   + %BlackBox()
MPRINT(BLACKBOX):   data _null_;
MPRINT(BLACKBOX):   put "This represents a complex process I want to abstract.";
MPRINT(BLACKBOX):   run;

This represents a complex process I want to abstract.

2   + %BlackBox()
MPRINT(BLACKBOX):   data _null_;
MPRINT(BLACKBOX):   put "This represents a complex process I want to abstract.";
MPRINT(BLACKBOX):   run;

This represents a complex process I want to abstract.

3   + %BlackBox()
MPRINT(BLACKBOX):   data _null_;
MPRINT(BLACKBOX):   put "This represents a complex process I want to abstract.";
MPRINT(BLACKBOX):   run;

This represents a complex process I want to abstract.