SAS-Makrovariablen auflösen

13

Die SAS-Programmiersprache ist eine klobige, archaische Sprache aus dem Jahr 1966, die bis heute verwendet wird. Der ursprüngliche Compiler wurde in PL / I geschrieben , und ein Großteil der Syntax stammt aus PL / I. SAS hat auch eine Präprozessor-Makrosprache, die sich ebenfalls von der von PL / I ableitet . In dieser Herausforderung werden Sie einige einfache Elemente der SAS-Makrosprache interpretieren.

In der SAS-Makrosprache werden Makrovariablen mit dem %letSchlüsselwort definiert und das Drucken in das Protokoll erfolgt mit %put. Anweisungen enden mit Semikolons. Hier sind einige Beispiele:

%let x = 5;
%let cool_beans =Cool beans;
%let what123=46.lel"{)-++;

Bei Makrovariablennamen wird die Groß- und Kleinschreibung nicht beachtet. Sie stimmen immer mit dem regulären Ausdruck überein /[a-z_][a-z0-9_]*/i. Für die Zwecke dieser Herausforderung werden wir Folgendes sagen:

  • Makrovariablen können nur Werte enthalten, die vollständig aus druckbaren ASCII-Zeichen bestehen der Ausnahme ; , &und%
  • Die Werte enthalten keine führenden oder nachfolgenden Leerzeichen
  • Die Werte dürfen nicht länger als 255 Zeichen sein
  • Werte können leer sein
  • Klammern und Anführungszeichen in den Werten stimmen möglicherweise nicht überein
  • Es kann eine beliebige Menge an Speicherplatz vor und nach der =in der%let Anweisung und dieser Platz sollte ignoriert werden
  • Vor dem Terminal ;in der %letAnweisung darf beliebig viel Platz sein, und dieser Platz sollte ebenfalls ignoriert werden

Wenn eine Makrovariable aufgerufen wird, wird sie in ihren Wert "aufgelöst". Makrovariablen werden durch Voranstellen aufgelöst &. Es ist eine optionale hintere ., die das Ende des Bezeichners angibt. Beispielsweise,

%put The value of x is &X..;

Schreibt The value of x is 5.in das Protokoll. Beachten Sie, dass zwei Zeiträume erforderlich sind, da ein einziger Zeitraum von verbraucht wird &X.und in aufgelöst wird 5. Beachten Sie auch, dass, obwohl wir xin Kleinbuchstaben definiert haben , dies &Xdasselbe ist wie&x da bei Makrovariablennamen die Groß- und Kleinschreibung nicht berücksichtigt wird.

Hier wird es schwierig. Es &können mehrere s aneinander gereiht werden, um Variablen aufzulösen, und &s können gleichzeitig die gleiche Verschachtelungsauflösung haben. Beispielsweise,

%let i = 1;
%let coolbeans1 = broseph;
%let broseph = 5;

%put &&coolbeans&i;  /* Prints broseph */
%put &&&coolbeans&i; /* Prints 5 */

Die innerste &Entschlossenheit zuerst, und die Entschlossenheit geht nach außen weiter. Der Abgleich von Variablennamen erfolgt gierig. In der zweiten %putAnweisung führt der Prozessor die folgenden Schritte aus:

  1. &ibeschließt 1, und die innerste Führung &wird verbraucht und gibt uns&&coolbeans1
  2. &coolbeans1beschließt broseph, uns zu geben&broseph
  3. &brosephbeschließt zu 5.

Wenn nachgestellte .s vorhanden sind , wird nur eine einzige .in der Auflösung verwendet, auch wenn mehrere vorhanden sind& s .

Aufgabe

Bei 1 bis 10 %letAnweisungen, die durch Zeilenumbrüche und eine einzelne Anweisung voneinander getrennt sind, können Sie %putdas Ergebnis der Anweisung ausdrucken oder zurückgeben%put Anweisung . Eingaben können auf jede übliche Art und Weise akzeptiert werden.

Sie können davon ausgehen, dass die Eingabe immer gültig ist und die %letAnweisungen der Anweisung vorausgehen %put. Definierte Variablen werden später nicht mehr neu definiert%let Anweisungen .

Wenn sie tatsächlich in SAS ausgeführt werden, gibt es keine Probleme mit Variablen, die in nicht vorhandene Variablen aufgelöst werden, und alles ist syntaktisch korrekt, wie oben beschrieben.

Beispiele

  1. Eingang:

    %let dude=stuff;
    %let stuff=bEaNs;
    %put &&dude..;
    

    Ausgabe:

    bEaNs.
    
  2. Eingang:

    %let __6 = 6__;
    %put __6&__6;
    

    Ausgabe:

    __66__
    
  3. Eingang:

    %let i=1;
    %let hOt1Dog = BUNS;
    %put &&HoT&i.Dog are FUNS&i!");
    

    Ausgabe:

    BUNS are FUNS1!")
    
  4. Eingang:

    %let x = {*':TT7d;
    %put SAS is weird.;
    

    Ausgabe:

    SAS is weird.
    
  5. Eingang:

    %let var1   =  Hm?;
    %let var11 = var1;
    %let UNUSED = ;
    %put &&var11.....;
    

    Ausgabe:

    Hm?....
    

    Beachten Sie, dass &&var11Übereinstimmungen, var11da die Namensübereinstimmung gierig ist. Wenn es eine gewesen war ., das heißt &&var1.1, dann var1würde angepasst werden und die zusätzlichen 1 würde nicht Teil eines Namens sein.

Das ist Codegolf, also gewinnt die kürzeste Lösung in Bytes!

Alex A.
quelle
Wie hat die Ausgabe von Testfall 1 eine Periode? Sollte &stuff.die Periode nicht entfernt werden?
GamrCorps
@GamrCorps Ich sollte Folgendes angeben: Bei der Auflösung wird nur eine einzige Nachlaufzeit benötigt.
Alex A.
@GamrCorps Bearbeitet, um es als Testfall anzugeben und hinzuzufügen.
Alex A.
so &&&&&&&&&a......................würde nach wie vor nur eine Periode entfernen?
GamrCorps
@GamrCorps Ja.
Alex A.

Antworten:

1

Python 3 , 354 341 336 Bytes

import re
S=re.sub
def f(x):
	r=x.splitlines();C=r[-1].strip('%put ');D=0
	while D!=C:
		D=C
		for a in sorted([l.strip('%let ').replace(" ","").split(';')[0].split('=')for l in r[:-1]],key=lambda y:-len(y[0])):
			s=1
			while s:C,s=re.subn('&'+a[0]+'(\.?)',a[1]+'😍\\1',S('😍+\.([^\.])','\\1',C),0,re.I)
	return S('😍+\.?','',C)

Probieren Sie es online!

edit: etwas leichtes kürzen

edit: sortiere in umgekehrter Reihenfolge nach -len (...) anstelle von [:: - 1] (5 Bytes), danke an Jonathan Frech!

Ungolfed

import re
S=re.sub # new name for the function re.sub()
def f(x):
    r=x.splitlines() # input string to list of rows
    C=r[-1].strip('%put ') # get the string to put (from the last row)
    D=0
    while(D!=C): # iterate until the result does not change
        D=C
        for a in                                                                                                                    : # iterate over the list of variables
                 sorted(                                                                          ,key=lambda y:len(y[0]),reverse=1) # sort list for greediness by decreasing var.name lengths
                        [l.strip('%let ') # cut the 'let' keyword
                                         .replace(" ","") # erase spaces
                                                         .split(';')[0] # cut parts after ';'
                                                                       .split('=') # create [variable_name,value] list
                                                                                  for l in r[:-1]] # for each row but last
            s=1
            while(s): # iterate until the result does not change
                C,s=re.subn( # substitute
                            '&'+a[0]+'(\.?)', # &varname. or &varname
                                                 a[1]+'😍\\1', # to value😍. or value😍
                                                              S('😍+\.([^\.])','\\1',C), # in the string we can get from C erasing (😍's)(.) sequences if the next char is not .
                                                                                        0,re.I) # substituting is case insensitive
    return S('😍+\.?','',C) # erase smileys and one .
mmuntag
quelle
Ich würde vorschlagen, viel auf der Python-Tipps-Seite zu nehmen . Triviale Optimierungen wie die Verkettung von nicht zusammengesetzten Anweisungen ( ;), die Reduzierung von Klammern ( if(...)-> if ...) und Listenoperationen ( ,reverse=1-> [::-1]) können leicht einige Bytes einsparen.
Jonathan Frech
Vielen Dank! Ich habe es schon einmal gelesen, aber es ist lange her und ich habe einige Tricks vergessen.
Mittwoch,
Bitte schön. len(y[0]))[::-1]kann sein -len(y[0])).
Jonathan Frech