2016-11-11 15 views
0

Um ein Programm zu refactorieren, habe ich einen komplexen Prozess, den ich abstrahieren möchte, in ein Makro eingefügt.SAS: Aufruf von Makro aus einer Datenschrittschleife

Der Prozess muss mehrmals hintereinander ausgeführt werden, daher ist es naheliegend, ihn in eine Schleife zu stellen.

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

Dies erzeugt jedoch den folgenden Fehler.

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

Was passiert?

Meine beste Schätzung ist, dass SAS versucht, einen Datenschritt innerhalb eines Datenschritts auszuführen.

Ich finde, dass ich diesen Fehler vermeiden kann, indem ich meine Schleife innerhalb eines Makros einschließe und dann das Makro sofort rufe.

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

All dies scheint wie eine Umgehungsart der Handhabung einer grundlegenden Programmieraufgabe. Ich hoffe, dass ich mehr darüber erfahren kann, warum der Data-Step-Ansatz fehlschlägt.

Bitte haben Sie Verständnis dafür, dass dies ein vereinfachtes Beispiel zur Veranschaulichung des Fehlers ist, dem ich begegne. Eine reale Instanz des Makros kann Argumente oder Rückgabewerte annehmen.

Antwort

1

Die Makrosprache ist ein Vorprozessor. Es erzeugt SAS-Code und wird ausgeführt, bevor der DATA-Schrittcode überhaupt kompiliert wird. Mit Code:

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

Die Makro% BlackBox() wird ausgeführt, einmal (nicht dreimal, weil es vor der DO-Schleife führt ausführt, konzeptionell außerhalb der DO-Schleife). Und der Daten Schritt Code wird:

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

Wie Sie sagen, es ist nicht möglich, in SAS ist ein Daten Schritt in einem anderen Daten Schritt auszuführen. Die Zeile data _null_ in Zeile 3 beendet den ersten Datenschritt und belässt ihn in einem nicht geschlossenen Do-Block.

Ich stimme @ Joe Punkte. Wenn Sie eine Anzahl von Makroaufrufen generieren möchten, ist das Verwenden einer Makro% DO-Schleife eine feine Vorgehensweise. Sein Artikel bietet einen guten Ansatz, um Daten zum Generieren von Makroaufrufen zu verwenden, indem Makrovariablen erstellt werden, die zu einer Liste von Makroaufrufen aufgelöst werden.

Ein weiterer nützlicher Ansatz zum Lernen ist CALL EXECUTE. Auf diese Weise können Sie einen Datenschritt zum Generieren von Makroaufrufen verwenden. CALL EXECUTE generiert die Makroaufrufe, wenn der Datenschritt ausgeführt wird, und die Makros werden außerhalb des Datenschritts ausgeführt (wenn Sie% NRSTR wie unten verwenden). Zum Beispiel:

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

Werden drei Makroaufrufe generieren:

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

Ihre Annahme ist genau richtig; SAS versucht, einen Datenschritt innerhalb eines Datenschritts auszuführen, und das wird natürlich nirgendwohin gehen (nun, es ist möglich, aber nur ... komplex).

Die Makro-Loop-Methode ist völlig in Ordnung, und ich würde für andere Programmiersprachen argumentieren, das ist im Grunde, was Sie tun würden. Sie schreiben eine Methode draw_box, um eine Box auf dem Bildschirm in C# anzuzeigen, dann schreiben Sie eine Methode draw_three_boxes, die drei Felder auf dem Bildschirm durch dreimaliges Aufrufen von draw_box anzeigt.

Nun, der Grund, warum es albern scheint ist, dass Sie schlechte Programmierung Design haben, wie die draw_three_boxes Methode ist sehr begrenzt: es ist nur in der Lage, eine Sache zu tun, zeichnen drei Boxen, also warum haben Sie nicht nur das Original draw_box Methode das an erster Stelle?

Vermutlich, was Sie tun sollten, ist schreiben draw_box und schreiben Sie dann draw_boxes(int count, int xpos, int ypos) oder so etwas, nicht wahr? Das selbe hier. Sie sollten das Makro PerformDoLoop() nicht so schreiben, wie Sie es getan haben, weil Sie die Häufigkeit der Ausführung der Schleife fest codieren.

Stattdessen erhalten Sie, warum Sie es dreimal ausführen. Wenn es wirklich etwas ist, das Sie nur wissen und kein Stück Daten ist, dann schreiben Sie %PerformDoLoop(count=) und rufen Sie dann %PerformDoLoop(count=3). Oder fügen Sie die %do-Schleife in das ursprüngliche Makro ein, mit einem Parameter für die Anzahl, standardmäßig auf eins.

Wahrscheinlicher ist, es gibt einen datengesteuerten Grund dafür, es dreimal zu tun. Du hast 3 Zustände. Sie haben 3 Klassen von Studenten. Was auch immer. Verwenden Sie das, um die Aufrufe an %BlackBox zu generieren. Das bringt Ihnen die besten Ergebnisse, denn dann machen Sie es nicht im Programm - Ihre Daten ändern sich, Sie bekommen sofort 2 oder 4 oder was auch immer Sie anrufen.

Sie können meine kürzlich vorgestellten Papier, Writing Code With Your Data von SESUG 2016 für weitere Informationen sehen, wie man das macht.

+0

Wie immer, danke, Joe. Um meine Aktionen zu erklären, ist das Makro '% PerformDoLoop' nicht dazu gedacht, eine Methode zum Ausführen der Schleife zu schreiben. Es ist buchstäblich die Schleife selbst durchzuführen. In jeder anderen Sprache kann eine Schleife im offenen Code aufgerufen werden. Nicht so in SAS. Eine Schleife muss stattdessen entweder in einen "Daten _null_" oder in einen Makro-Mend-Call-Block wie oben beschrieben eingewickelt werden. In Wirklichkeit nimmt meine BlackBox ein Argument. Da ich BlackBox für verschiedene Argumente aufrufen muss, habe ich eine Makroliste (als Pseudo-Array) erstellt. BlackBox wird dann ein anderes Argument unter Verwendung des Arrays und des do-Schleifenindexes zugeführt. –

+0

@LoremIsump Wenn Sie das tun, dann würde ich das Schleifenmakro nicht schreiben - anstatt die Makroliste der Parameter zu erstellen, erstellen Sie einfach eine Makroliste der Aufrufe von BlackBox. Ich würde auch vorschlagen, dass SAS nicht besonders einzigartig darin ist, keine Schleifen in offenem Code zu haben; Sie könnten das auch nicht in C machen, für ein besonders offensichtliches Beispiel müsste es in einer Subroutine sein. Das Gleiche gilt für SAS, es ist nur eine erzwungene Code-Kapselung. Das heißt, in SAS 9.5 sieht es so aus, als würden sie _die Makrosteuerung (% do usw.) außerhalb von Makros erlauben, obwohl ich nicht überzeugt bin, dass das eine gute Sache ist. – Joe

+0

Interessanter Punkt über 9.5. Ich hatte bei SGF gehört, dass die Open Statement% IF in 9.5 kam. Und stimme zu, ich bin mir nicht sicher, ob es auch gut ist. Haben Sie schon einmal darüber nachgedacht, wie es umgesetzt wird? – Quentin