为了重构一个程序,我采用了一个复杂的过程,我想要抽象并将它放在一个宏中。
%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;
所有这些似乎都是处理基本编程任务的迂回方式。我希望更多地了解数据步骤方法失败的原因,这将让我深入了解如何更优雅地完成此任务。
请理解这是一个简化的示例,用于说明我遇到的错误。宏的实例可以包含参数或返回值。
答案 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.