Eigene Makrofunktionen

Aus SAS-Wiki
Wechseln zu: Navigation, Suche

Von Grischa Pfister: Hans-Peter Altenburg, Heinrich Stürzl, Almut Hahn, Carina Ortseifen, Grischa Pfister: Tipps und Tricks für den leichteren Umgang mit der SAS Software, KSFE 2008

Eigene Makrofunktionen erlauben es, Makro-Code wesentlich effizienter zu schreiben. Zum einen können oft verwendete SAS/BASE-Funktionen auf diese Weise gekapselt und in der Makrosprache verfügbar gemacht werden, zum anderen können typische Abläufe oder Aufgaben zu einer Funktion zusammengefasst werden. Eine Makrofunktion ist zunächst ein ganz normales Makro, allerdings unterliegt es bestimmten Voraussetzungen. Dafür besitzt eine Makrofunktion aber einen Rückgabewert, der dann in Zuweisungen oder bedingten Verzweigungen abgefragt werden kann:

%Let wert = %Funktion();
%If ( %Funktion() ) %Then …

Ein Teil der SAS-eigenen Makrofunktionen ist so implementiert, unter !sasroot/core/sasmacro finden sich z.B. die Programme für %Trim und %Lowcase.

Das Grundprinzip der Makrosprache ist die Textersetzung, und dieses Prinzip steht auch hinter den Makrofunktionen. Der Rückgabewert ist in Wirklichkeit ein String, der übrig bleibt, wenn das Makro vom Makroprozessor verarbeitet worden ist. Dieser String wird dann an den Wordscanner zurückgegeben und an die Stelle des Makroaufrufes gesetzt. Aus diesem Grunde darf eine Makrofunktion keinen Text außer dem Rückgabewert erzeugen – anders formuliert bedeutet das, dass das Makro weder einen Data Step noch Prozeduren verwenden darf, denn das wäre „normaler“ Base-Code, der vom Makroprozessor zurück an den Wordscanner geht (zur Arbeitsweise von Wordscanner und Makroprozessor siehe [1], ab Seite 36). Dafür stehen aber (fast) alle SAS/BASE Funktionen zur Verfügung und damit lässt sich allerhand anstellen. Ein einfaches Beispiel sieht so aus:

%Macro bsp01;
  Textersetzung
%Mend;
%Put Das Grundprinzip ist %Bsp01 !;

Log: Das Grundprinzip ist Textersetzung !

Das Makro enthält lediglich eine Textkonstante. Wenn der Code abgeschickt wird, wird zunächst das Makro kompiliert und dann das %PUT-Statement verarbeitet. Der Word-Scanner erkennt dabei den Makroaufruf %BSP01 und ruft den Makroprozessor auf. Der führt das Makro aus und setzt den Ergebnis-String „Textersetzung“ an die Stelle des Makroaufrufes. Wichtig ist, dass die Zeichenkette „Textersetzung“ nicht mit einem Semikolon beendet wird. Es handelt sich hier nicht um eine Code-Zeile, sondern um den Rückgabewert, das Semikolon würde kein Statement abschließen, sondern wäre Teil des Wertes!! Das Semikolon würde also im Wordscanner landen und dort das eigentliche Programm durcheinanderbringen.

Das Prinzip von Makrofunktionen ist also sehr einfach, die Einsatzmöglichkeiten dafür umso größer, deshalb noch ein paar Beispiele.

* GP Pfad von Library/Fileref abfragen *;
%Macro GetPath(ref);
  %Local path;
  %Let path = %Sysfunc(pathname(&ref));
  &path
%Mend;

%Put WORK wird gespeichert unter %GetPath(work);

Oft sollen in Anwendungen temporäre Dateien abgelegt werden, wofür sich das Verzeichnis der WORK-Bibliothek anbietet. Um an diesen Pfad heranzukommen gibt es auch eine SAS/BASE Funktion namens pathname(). Die Makrofunktion %GetPath() kapselt den Zugriff auf die Funktion und kann z.B. auch verwendet werden um zu prüfen, ob eine Bibliothek überhaupt vorhanden ist. Es ist üblich, den Rückgabewert zunächst in eine lokale Makrovariable zu speichern. Das macht es zum einen einfacher, Werte in mehreren Schritten zusammenzufassen, zum anderen stellt die lokale Makrovariable sicher, dass nicht unbeabsichtigt im übergeordneten Makro eine Makrovariable überschrieben wird (vergl. [1] ab Seite 42).

%* GP Erzeugen eines Timestamp *;
%Macro timestamp;
  %Local ts;
  %Let ts = %Sysfunc(date());
  %Let ts = %Sysfunc(putn(&ts,ddmmyyd10.));
  %Let ts = &ts._%Sysfunc(hour(%Sysfunc(time())))_  [Umbruch 
            %Sysfunc(minute(%Sysfunc(time())));      layoutbedingt!]
  &ts
%Mend;

%Put Der aktuelle Zeitstempel ist: %Timestamp;

Ein weiteres typisches Beispiel sind Zeitstempel, die z.B. für die Benennung von Dateien gebraucht werden. Durch Hinzufügen von Parametern kann das Makro z.B. so erweitert werden, dass es nur Datum oder nur Uhrzeit zurückgibt. Genauso gut könnte das Makro verschieden formatierte Versionen des Zeitstempels liefern, hier sind viele Varianten denkbar.

%* GP Anzahl der Obs in Tabelle abfragen *;
%Macro Nobs(table);
  %Local dsid nobs;
  %Let dsid = %Sysfunc(open(&table));
  %If ( &dsId ) %Then %Do;
    %Let nobs = %Sysfunc(attrn(&dsid,nlobs));
    %Let dsid = %Sysfunc(close(&dsid));
  %End;
 %Else %Let nobs = -1;
 &nobs
%Mend;

%Put Sätze in Tabelle Sashelp.Class: %Nobs(Sashelp.Class);
%Put Sätze in Tabelle Sashelp.Shoes: %Nobs(Sashelp.Shoes);

Das letzte Beispiel verwendet SAS/BASE Funktionen, die ursprünglich aus der SCL (SAS Component Language) stammen. Mit deren Hilfe kann eine SAS-Tabelle geöffnet und Information aus dem Header ausgelesen werden (und einiges mehr, eine Übersicht der Funktionen für den Zugriff auf SAS-Tabellen gibt es in [2] ab Seite 304). Da eine Makrofunktion weder einen Data Step noch eine Prozedur ausführen kann, ist es auf diesen Wegen nicht möglich, die Anzahl der Beobachtungen zu ermitteln. Mit Hilfe der SCL-Funktionen geht es aber doch. So kann z.B. ganz einfach geprüft werden, ob ein vorangegangener Schritt auch wirklich Beobachtungen in eine Ergebnistabelle geschrieben hat oder nicht.

Literatur

  • SAS 9.1 Macro Language – Reference.
  • SAS 9.1.3 Language Reference: Dictionary, fifth Edition.