2016-11-11 105 views
0

爲了重構程序,我花了一個複雜的過程來抽象並將其放入宏中。SAS:從數據步驟循環內調用宏

%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; 

所有這些看起來像是一個處理基本編程任務的迂迴路線。我希望更多地瞭解爲什麼數據步驟方法失敗會讓我深入瞭解如何更優雅地完成此任務。

請明白,這是用來說明我遇到的錯誤一個簡單的例子。宏的實際實例可能需要參數或返回值。

回答

1

宏語言是一個預處理器。它生成SAS代碼,並在DATA步驟代碼編譯之前執行。與您的代碼:

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

宏%黑匣子()將執行一次(不三次,因爲它執行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. 
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班學生。隨你。用它來生成​​的呼叫。這會給你帶來最好的結果,因爲那時你並沒有在程序中這麼做 - 你的數據會發生變化,你會立即得到2或4或任何呼叫。

你可以在SESUG 2016上看到我最近發表的論文Writing Code With Your Data,瞭解更多關於如何做到這一點的信息。

+0

與往常一樣,謝謝你,喬。爲了解釋我的行爲,宏'%PerformDoLoop'不打算類似於編寫執行循環的方法。從字面上看,執行循環本身。在任何其他語言中,可以在開放代碼中調用循環。在SAS中不是這樣。相反,必須將一個循環包裝在一個'data _null_'中或者一個宏修補程序塊中,如上所述。 實際上,我的BlackBox需要一個參數。由於我需要爲BlackBox調用不同的參數,因此我創建了一個宏列表(作爲僞數組)。然後BlackBox使用數組和循環索引爲不同的參數提供參數。 –

+0

@LoremIpsum如果你打算這樣做,那麼我不會寫循環宏 - 而是創建參數的宏列表,只需創建一個調用BlackBox的宏列表。 我也建議SAS沒有在開放代碼中沒有循環特別獨特;你也不能在C中做到這一點,對於一個特別明顯的例子,它必須在子程序中。 SAS也一樣,它只是強制代碼封裝。也就是說,在SAS 9.5中,它看起來像是允許宏之外的宏流控('%do'等),儘管我不相信這是件好事。 – Joe

+0

有趣點約9.5。我曾在SGF聽到%IF的公開聲明將在9.5。並同意我也不確定這是否是件好事。你有沒有看到有關它將如何實施的早期寫作? – Quentin