Print, Increment, Decrement, Alias ​​- Prindeal interpretieren


30

Prindeal (ausgesprochen prin-dee-al ) ist eine neue esoterische Programmiersprache, die nur vier Befehle enthält: pr int , in crement , de crement und al ias . Trotz seines Minimalismus können komplexe mathematische Operationen in Prindeal durchgeführt werden, indem die vier Befehle geschickt kombiniert werden.

Ihre Aufgabe bei dieser Code-Golf- Herausforderung ist es, das kürzeste Programm zu schreiben, das Prindeal-Code ausführen kann.

Die Spezifikation ist lang, aber ich habe versucht, sie so deutlich wie möglich zu machen, und ich glaube, wenn Sie sich die Mühe machen, Prindeal zu lernen, werden Sie feststellen, dass es ziemlich elegant ist!


Prindeal interpretieren

Vorverarbeitung

Bevor ein Prindeal-Programm interpretiert werden kann, müssen diese Dinge in dieser Reihenfolge entfernt werden:

  1. Alles, was nach einem #Zeichen bis zum Ende der Zeile steht, ist eingeschaltet, plus das #Selbst. (Dies sind Kommentare.)
  2. Leerzeichen in jeder Zeile nachstellen.
  3. Völlig leere Zeilen.

Zum Beispiel das Prindeal-Programm

p cat #The next line has 7 trailing spaces.
p dog       

#p mouse

würde vorverarbeitet werden

p cat
p dog

Ab hier gehen wir davon aus, dass dieser Vorverarbeitungsschritt abgeschlossen ist.

Variablen

Wir müssen schnell Variablen definieren, bevor wir zeigen, wie sie verwendet werden.

Variablen (und Verweise auf Variablen) werden an die Argumente von Prindeal-Befehlen übergeben. Variablen sind immer global , sodass Änderungen an einer Variablen, unabhängig davon, wo sie auftreten, überall wiedergegeben werden.

Jede Variable enthält eine nicht negative Ganzzahl mit beliebiger Genauigkeit (0, 1, 2, 3, ...). Variablen müssen nicht vorinitialisiert werden - sie beginnen immer mit dem Wert 0, wenn sie zum ersten Mal verwendet oder aufgerufen werden.

Ein Variablenname kann eine beliebige nicht leere Zeichenfolge aus alphanumerischen Zeichen und Unterstrichen sein, die nicht mit einer Ziffer beginnt - [a-zA-Z_][0-9a-zA-Z_]*in Regex . Es wird zwischen Groß- und Kleinschreibung unterschieden, spiny_lumpsuck3rund Spiny_lumpsuck3res handelt sich um verschiedene Variablen.

Ausführung

Prindeal ist eine zwingende Programmiersprache. Wenn ein Prindeal-Programm ausgeführt wird, werden seine Anweisungen der Reihe nach von oben nach unten ausgeführt und das Programm endet.

Jede nicht eingerückte Zeile in einem Prindeal-Programm ist eine Anweisung, die die Ausführung eines einzelnen Befehls beinhaltet, der Argumente annehmen kann oder nicht.

Indented Linien treten erst nach Alias - Befehle. Insbesondere treten genau drei Zeilen, die mit einzelnen Leerzeichen eingerückt sind, nach jedem Alias- Befehl auf und werden als Teil davon betrachtet. So alias Aussagen sind wirklich lange vier Linien. (Es kann sich um eine Zeile handeln, vier sind lediglich lesbarer.)

Non- Alias- Anweisungen

Mit Ausnahme des Alias hat jede Anweisung in einem Prindeal-Programm die Form:

[command name] [argument 1] [argument 2] [argument 3] ...

Es kann eine beliebige Anzahl von Argumenten geben (einschließlich überhaupt keiner). Jedes Argument ist immer eine Variable oder (wie wir bei der Beschreibung des Alias ​​sehen werden ) ein Verweis auf eine Variable .

Sobald die Ausführung abgeschlossen ist, wird jede Anweisung als fehlgeschlagen oder erfolgreich markiert, je nachdem, ob Fehler aufgetreten sind oder nicht. (Dies ist nur dann wirklich wichtig, wenn wir uns mit der Verwendung von Alias befassen .)

Die integrierten Funktionen print , increment und decrement sind Anweisungen mit der obigen Form. Folgendes machen sie:

  1. print hat einen Befehlsnamen pund akzeptiert ein Argument. Es gibt den Namen der übergebenen Variablen und ihren Wert (in Dezimalzahl) aus, getrennt durch "=", gefolgt von einer neuen Zeile. Es wird immer als Erfolg gewertet .

    Zum Beispiel das Prindeal-Programm

    p _MyVariable_321
    p screaming_hairy_armadillo
    

    würde ausgeben

    _MyVariable_321 = 0
    screaming_hairy_armadillo = 0
    

    weil alle Variablen bei 0 beginnen. (Die Leerzeichen vor und nach dem Gleichheitszeichen sind erforderlich.)

  2. increment hat den Befehlsnamen iund nimmt ein Argument entgegen. Es erhöht den Wert der übergebenen Variablen um 1. Es wird immer als Erfolg gewertet.

    Zum Beispiel das Programm

    i alpaca
    p alpaca
    i alpaca
    p alpaca
    

    würde ausgeben

    alpaca = 1
    alpaca = 2
    

    Beachten Sie, wie alpacavon 0 auf 1 inkrementiert wurde, obwohl noch nie zuvor darauf zugegriffen wurde.

  3. decrement hat einen Befehlsnamen dund benötigt ein Argument. Wenn die Variablen übergeben ist ungleich Null sein Wert um 1 verringert wird und die Anweisung wird als markiert Erfolg . Wenn die übergebene Variable 0 ist, wird nichts unternommen und die Anweisung als fehlerhaft gekennzeichnet .

    Zum Beispiel das Programm

    i malamute
    p malamute
    d malamute    #success
    p malamute
    d malamute    #failure
    p malamute
    d akita       #failure
    p akita
    

    würde ausgeben

    malamute = 1
    malamute = 0
    malamute = 0
    akita = 0
    

    Beachten Sie, dass das Dekrementieren einer Variablen mit dem Wert 0 die einzige Möglichkeit ist, einen Fehler zu erzeugen .

Die Alias- Anweisung und die Alias-Befehle

Der Alias- Befehl hat eine spezielle Syntax und ist am leistungsfähigsten, da er zum Definieren neuer Befehle verwendet werden kann. Der Name des Aliasbefehls lautet aund eine Aliasanweisung hat die Form:

a [name of new command]
 [statement A]
 [statement B]
 [statement C]

Wobei jedes [statement X]eine Nicht- Alias- Anweisung darstellt, dh etwas mit der Form [command name] [argument 1] [argument 2] [argument 3] ....

Der Name des Befehls mit Alias [name of new command]kann eine beliebige nicht leere Zeichenfolge aus alphanumerischen Zeichen und Unterstrichen sein, die nicht mit einer Ziffer beginnt - [a-zA-Z_][0-9a-zA-Z_]*in Regex.

(Dies ist der gleiche Satz von Namen wie Variablen, aber Alias- Befehle und Variablen werden an verschiedenen Stellen unterschiedlich verwendet . Eine Variable kann den gleichen Namen wie ein Befehl haben, ohne dass dies negative Folgen hat.)

Wenn eine Alias- Anweisung ausgeführt wird, wird neben den ursprünglichen vier p i d aBefehlen ein neuer Befehl hinzugefügt . Der neue Befehl kann [command name]wie jeder andere Nicht- Alias- Befehl als in-Anweisung verwendet und mit Argumenten aufgerufen werden .

Wenn eine Anweisung mit einem Alias-Befehlsnamen ausgeführt wird, werden genau zwei weitere Anweisungen aus der ursprünglichen Alias- Anweisung ausgeführt:

  • [statement A] wird immer ausgeführt
  • [statement B]wird ausgeführt, wenn [statement A]ein Erfolg war
  • [statement C]wird ausgeführt, wenn [statement A]ein Fehler aufgetreten ist

Anweisungen A, B und C werden immer träge ausgeführt , dh sie werden sofort zum Zeitpunkt der Ausführung ausgewertet.

Wenn Sie fertig ausgeführt wird , wird der Alias - Befehl mit dem gleichen gekennzeichnet Erfolg oder Versagen Flagge als Aussage B oder C, je nachdem , welche ausgeführt wurde . ( Alias- Anweisungen selbst müssen nicht markiert werden, da sie nicht in sich selbst vorkommen können.)

Alias ​​Beispiel 1

Angenommen, wir möchten einen neuen Befehl, der die Variable frogzweimal inkrementiert . Diese Alias-Anweisung erreicht es:

a increment_frog_twice
 i frog
 i frog
 d frog

Anweisung A ( i frog) wird immer ausgeführt und immer als Erfolg gekennzeichnet, daher wird auch Anweisung B ( i frog) ausgeführt und die Variable frog wird daher um 2 erhöht. Der increment_frog_twiceBefehl wird immer als Erfolg gekennzeichnet, da Anweisung B immer ausgeführt wird und B immer a ist erfolg . Anweisung C ( d frog) wird niemals ausgeführt.

Also die Ausgabe an

a increment_frog_twice
 i frog
 i frog
 d frog
p frog
increment_frog_twice
p frog

wäre

frog = 0
frog = 2

Wir können dieses Beispiel so verallgemeinern, dass jede Variable zweimal inkrementiert werden kann, indem Sie dem Alias-Befehl ein Argument geben.

In einer Alias- Anweisung stehen die positiven Ganzzahlen 1, 2, 3 usw. für die Argumente 1, 2, 3 usw., die an den Alias-Befehl übergeben werden. (Diese Argumente können einfache Variablen oder Verweise auf Variablen selbst sein.) Diese Zahlen können nur in den Argumenten von Anweisung A, B und C in einer Alias- Anweisung vorkommen. Es macht keinen Sinn, wenn sie woanders auftauchen.

Alias ​​Beispiel 2

Dies verallgemeinert das letzte Beispiel: Jede Variable, an die übergeben increment_twicewird, wird um 2 inkrementiert, da 1es sich um einen Verweis auf das erste übergebene Argument handelt:

a increment_twice
 i 1
 i 1
 d 1 #never reached
p toad
increment_twice toad
p toad

Die Ausgabe dieses Programms wäre

toad = 0
toad = 2

Wir könnten dann einen anderen Befehl aliasen, der zwei Argumente aufnimmt und increment_twicebeide aufruft :

a increment_twice
 i 1
 i 1
 d 1 #never reached
a increment_both_twice
 increment_twice 1
 increment_twice 2
 d 1 #never reached
increment_both_twice platypus duck
p platypus
p duck

Die Ausgabe hier wäre

platypus = 2
duck = 2

Es ist wichtig zu wissen, dass Alias-Befehle rekursiv sein können, da hier ihre wahre Kraft liegt. Zum Beispiel können wir einen Befehl erstellen, der jede übergebene Variable auf 0 setzt:

Alias ​​Beispiel 3

Der set_to_zeroBefehl akzeptiert ein Argument und setzt seine Variable auf 0. Wenn er fertig ist, wird er als erfolgreich markiert :

a set_to_zero
 d 1
 set_to_zero 1
 i _dummy_
i oryx
i oryx
i oryx
p oryx
set_to_zero oryx
p oryx

Die Ausgabe dieses Programms wäre

oryx = 3
oryx = 0

Was passiert ist, dass, wenn ausgeführt set_to_zero oryxwird, d 1erfolgreich oryxvon 3 auf 2 dekrementiert wird , dann set_to_zero 1aufgerufen wird, was dasselbe ist wie ein set_to_zero oryxerneuter Aufruf . Der Prozess wird wiederholt, bis d 1ein Fehler aufgetreten ist. Die Rekursion wird gestoppt und die _dummy_Variable erhöht , sodass ein Erfolg erzielt wird.


Herausforderung

Schreiben Sie ein Programm, das Prindeal-Code genau wie oben beschrieben ausführen kann. Übernehmen Sie den Prindeal-Code über stdin, die Befehlszeile oder als Textdatei. Drucken Sie die Ausgabe des Prindeal-Programms auf stdout oder die nächstgelegene Alternative Ihrer Sprache.

Alternativ können Sie eine Funktion schreiben, die den Code als Zeichenfolge aufnimmt und die Ausgabezeichenfolge ausgibt oder zurückgibt.

Darüber hinaus können Sie davon ausgehen, dass:

  • Der eingegebene Prindeal-Code enthält nur Zeilenumbrüche und druckbare ASCII-Zeichen und endet (optional) mit einer Leerzeile.
  • Der eingegebene Code ist gültig Prindeal - wohlgeformt und syntaktisch korrekt.
  • Das Ausführen des Codes führt weder zu Endlosschleifen noch zu ungültigen Verweisen auf Befehle, die nicht definiert wurden, oder auf Argumente, die nicht angegeben wurden.
  • Die Befehlsnamen p, i, d, und awerden nie über aliased werden. (Sie können nicht davon ausgehen, dass Variablen diese Namen nicht haben.)

Außerdem spielt es keine Rolle, ob Ihre Variablenwerte wirklich Ganzzahlen mit willkürlicher Genauigkeit sind, da nur Zahlen mit weniger als etwa 1000 getestet werden. Es ist auch in Ordnung, wenn Ihre Sprache über Rekursionsbeschränkungen (wie Python ) verfügt, auf die komplexere Prindeal-Programme möglicherweise stoßen, solange das folgende Testprogramm funktioniert.

Testprogramm

Hier ist ein großes Prindeal-Programm, das die Operationen Addition, Multiplikation und Exponentiation durch die Verwendung von Dummy-Variablen (beginnend mit _Konvention) und vielen Helfer-Aliasen aufbaut :

#Command Definitions:
a s             #flag as a success
 i _
 d _
 d _
a f             #flag as a failure
 d _
 d _
 d _
a z             #1 = zero
 d 1
 z 1
 s
a n             #1 = one
 z 1
 i 1
 s
a move          #2 += 1, 1 = zero
 moveH 1 2
 move 1 2
 s
a moveH         #move helper
 d 1
 i 2
 f
a dupe          #2 += 1, 3 += 1, 1 = zero
 dupeH1 1 2 3
 dupe 1 2 3
 s
a dupeH1        #dupe helper
 d 1
 dupeH2 2 3
 f
a dupeH2        #dupe helper
 i 1
 i 2
 s
a copy          #2 = 1
 z 2
 copyH 1 2
 s
a copyH         #copy helper
 dupe 1 2 _copy
 move _copy 1
 s
a addTo         #1 += 2
 copy 2 _add
 #testing comments #
 move _add 1#in weird places # just because #
 s
#it's a g##d idea
###
a add           #1 = 2 + 3
 #its a good idea
 z 1
 addH 1 2 3
 s
##

#
a addH          #add helper
#this is a comment
 addTo 1 2 #as is this
 addTo 1 3
 s
a mul           #1 = 2 * 3
 mulH1 1 2
 mulH2 1 3
 s
a mulH1         #mul helper
 z 1
 copy 2 _mul
 s
a mulH2         #mul helper
 mulH3 1 2
 mulH2 1 2
 s
a mulH3         #mul helper
 d _mul
 addTo 1 2
 f
a mulBy         #1 *= 2
 mul _mulBy 1 2
 copy _mulBy 1
 s
a pow           #1 = 2^3
 powH1 1 3
 powH2 1 2
 s
a powH1         #pow helper
 n 1
 copy 2 _pow
 s
a powH2         #pow helper
 powH3 1 2
 powH2 1 2
 s
a powH3         #pow helper
 d _pow
 mulBy 1 2
 f

#Running Tests:
p A
p B
p C
n A         #A = 1
n B         #B = 1
add C A B   #C = A + B = 1 + 1 = 2
p ____
p A
p B
p C
add B A C   #B = A + C = 1 + 2 = 3
p ____
p A
p B
p C
mul d B C   #d = B * C = 3 * 2 = 6
p ____
p d
mulBy d B   #d = d * B = 6 * 3 = 18
p ____
p d
d A         #A = A - 1 = 1 - 1 = 0
mulBy d A   #d = d * A = 18 * 0 = 0
p ____
p d
pow A C B   #A = C ^ B = 2 ^ 3 = 8
p ____
p A
p B
p C
pow A B C   #A = B ^ C = 3 ^ 2 = 9
p ____
p A
p B
p C
pow C A B   #C = A ^ B = 9 ^ 3 = 729
p ____
p A
p B
p C

(Wenn Sie mit diesem Code herumspielen, beachten Sie, dass viele der Befehle fehlschlagen, wenn dieselbe Variable mehrmals als Argument angegeben wird. Dies kann leicht behoben werden, der resultierende Code ist jedoch länger.)

Ihr Prindeal-Interpreter sollte in der Lage sein, die genaue Ausgabe zu erstellen:

A = 0
B = 0
C = 0
____ = 0
A = 1
B = 1
C = 2
____ = 0
A = 1
B = 3
C = 2
____ = 0
d = 6
____ = 0
d = 18
____ = 0
d = 0
____ = 0
A = 8
B = 3
C = 2
____ = 0
A = 9
B = 3
C = 2
____ = 0
A = 9
B = 3
C = 729

Wertung

Der kürzeste Code in Bytes gewinnt. Tiebreaker geht auf frühere Vorlage.

Brownie Bonus: Schreiben Sie ein cooles Programm in Prindeal. Ich habe Addition und Multiplikation implementiert. Können Sie subtrahieren oder dividieren?


Oh man, ich denke mal ich lasse den Pyth in Ruhe und ziehe ein Lisp raus! Eine Frage: Funktionen und Variablen leben in völlig unterschiedlichen Namensräumen, oder? Also kann ich inkrementieren p, und p pwas würde dann 1 ausgeben, richtig?
Orlp

@orlp Richtig. (Es gibt einige Anmerkungen dazu.)
Calvins Hobbys

2
Ich kann nicht der einzige sein, der PRNDL denkt, wenn ich den Namen der Sprache sehe.
Downgoat

Gibt es eine maximale Anzahl von Argumenten, die an einen Alias-Befehl übergeben werden?
Zach Gates

@ ZachGates Nope
Calvins Hobbys

Antworten:


9

Pyth, 162 136 Bytes

JfTmchcd\#).zKHW<ZlJI!e=T@J~+Z1=@Tk)=k0 .x=J+]h=Nm.xL@Tskd@K=NhT+]+tN0>J~Z0,=Y.x@H=eT0?qN\pps[Td\=dYb)?xGN?qN\iXHThY?YXTH_1=k1XKT:JZ=+Z3

Demonstration.

26 Zeichen durch Inlining von Variablen und Ändern von Iund Ebasierendem Kontrollfluss zu ?und .xbasierendem Kontrollfluss herausgearbeitet.

Zum ersten Mal gingen mir in Pyth die Variablen aus. Jede einzelne Variable in Pyth ( bdkGHNTYund JK) wurde verwendet, und ich wollte verwendenb als Zeilenumbruch verwenden. Zum Glück konnte ich verwendenN zwei völlig verschiedene Dinge in verschiedenen Teilen des Programms bedeuten, und so funktioniert es immer noch.

Ungolfed (laufen mit -m):

JfTmchcd\#).z
KH
W<ZlJ
  I!e=T@J~+Z1
    =@Tk)
  =k0
     .x
      =J+]h=Nm.xL@Tskd@K=NhT+]+tN0>J~Z0
      ,
        =Y.x@H=eT0
        ?qN\p
          ps[Td\=dYb)
          ?xGN
            ?qN\i
              XHThY
              ?Y
                XTH_1
                =k1
            XKT:JZ=+Z3

3
Ich liebe es, wie ich immer noch nicht sagen kann, was es macht, selbst wenn es ungolfed ist ...
Jerry Jeremiah

Nun, das Fazit ist, dass Pyth nicht vollständig ist ...
Erik the Outgolfer,

8

Python 2, 600 584 397 373 Bytes

Dies ist meine eigene Referenzlösung für den Golfsport. Jeder ist willkommen, es zu verbessern oder seiner Logik in seiner eigenen Antwort zu folgen, solange die Zuschreibung erfolgt.

Das Schöne daran ist, dass keine Rekursion durchgeführt wird, sodass es nie Probleme mit der Rekursionsgrenze von Python geben wird. Beispielsweise kann das Countup Prindeal-Programm von Sp auf unbestimmte Zeit ausgeführt werden.

p=filter(len,[l.split('#')[0].split()for l in input().split('\n')]);m={};v={};i=0
while i<len(p):
 s=p[i]
 if'('in`s`:s=s[f]
 n,f=s[0],0
 if n in m:a,b,c=([s[int(g)]if g.isdigit()else g for g in t]for t in m[n]);p=[a,(b,c)]+p[i+1:];i=0;continue
 s=s[1]
 q=v.get(s,0)
 if'd'>n:m[s]=p[i+1:i+4];i+=3
 elif'i'<n:print s,'=',q
 elif'd'<n:v[s]=q+1
 elif q:v[s]-=1
 else:f=1
 i+=1

Es ist ein Programm, das die angegebene Programmzeichenfolge mit Zeilenumbrüchen einliest, z
'p _MyVariable_321\np screaming_hairy_armadillo'.

Aus den Antworten von Sp und Pietu habe ich verschiedene Hinweise zum Golfen entnommen . Danke Leute :)


6

Python 3, 345 336 335 328 Bytes

a=0
A={}
V={}
def f(l):
 if l[0]in"d p i":c,u=l;U=V[u]=V.get(u,0)+"pi".find(c);S=U<0;V[u]+=S;c<"p"or print(u,"=",U)
 else:d=lambda q:[w.isdigit()and l[int(w)]or w for w in A[l[0]][q]];S=f(d(1+f(d(0))))
 return S
for z in open("P"):
 l=z.split("#")[0].split()
 if"a "==z[:2]:a,s,*x=3,l[1]
 elif l*a:x+=l,;a-=1;A[s]=x
 elif l:f(l)

(-6 Bytes dank @orlp)

Immer noch Golf spielen. Angenommen, das Programm ist in einer Datei mit dem Namen gespeichertP .

Die Anrufe ins fLambda steckend würde einige Bytes sparen, aber der letzte Testfall würde die maximale Rekursionstiefe erreichen.

Einige Prindeal-Programme

Das nutzlose Subtraktionsprogramm

Hier ist ein nutzloses Subtraktionsprogramm . Es ist nutzlos, weil es Erfolg / Misserfolg nicht entsprechend zurückgibt, obwohl es richtig subtrahiert.

Die Ausgabe sollte sein:

a = 15
b = 6
__________ = 0
a = 9
b = 6

Zusammenzählen

a helper
 p 1
 countup 1
 i success

a countup
 i 1
 helper 1
 d failure

countup n

Zählt nach oben und druckt nfür immer. Könnte möglicherweise als Test für die Interpretergeschwindigkeit dienen (Vorsicht vor den langen Tracebacks bei Tastatur-Interrupts).


2
Jeder auf dieser Frage hat diesen Golf verpasst, ich verstehe nicht warum. l[:(l+"#").find("#")]und alle seine Variationen können durch eine einfache ersetzt werden l.split('#')[0].
Orlp

@orlp War so fokussiert, finddass ich vergessen habe, dass man es könnte, splitauch wenn #es nicht da wäre. Vielen Dank :)
Sp3000

6

JavaScript (ES6), 273 258

Behobene Fehler bearbeiten und eine echte Testsuite hinzugefügt.

Ohne führende Leerzeichen und Zeilenumbrüche.

Sicher kann man ein bisschen mehr golfen.

Ich bin zu müde, jetzt eine Erklärung zu schreiben, und denke, es ist ein gutes Beispiel für die Verwendung von Closures, um temporäre Werte (Parameter) am Leben zu erhalten.

Testlauf des Snippets in jedem EcmaScript 6-kompatiblen Browser (insbesondere nicht in Chrome und nicht in MSIE. Ich habe es in Firefox getestet, Safari 9 könnte funktionieren)

F=p=>(
  p=p.match(/^[^#\n]+/gm).filter(r=>r.trim(o='',v=[])),
  s={
    '':_=>1,
    p:a=>o+=a+` = ${v[a]||0}\n`,
    i:a=>v[a]=-~v[a],
    d:a=>v[a]&&v[a]--,
    a:(n,j)=>s[n]=(u,t,a)=>x(p[!x(p[j+1],0,a,1)+j+2],0,a,1)
  },
  p.map(x=(r,i,w,l,a=r.split(/ +/).slice(l).map(x=>-x?w[x]:x))=>s[a[0]](a[1],i,a)),
  o
)

// TEST

$('#O tr').each(function() {
  var $cells = $(this).find('td')
  var prg = $cells.eq(0).text()
  console.log(prg)
  var output = F(prg)
  $cells.eq(1).text(output)
})
#O td { vertical-align:top; white-space: pre; border: 1px solid #888; font-family:monospace }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<table>
<tr><th>Program</th><th>Outpout</th></tr>
<tbody id=O>  
<tr><td>p _MyVariable_321
p screaming_hairy_armadillo</td><td></td></tr>
<tr><td>i alpaca
p alpaca
i alpaca
p alpaca</td><td></td></tr>
<tr><td>i malamute
p malamute
d malamute    #success
p malamute
d malamute    #failure
p malamute
d akita       #failure
p akita</td><td></td></tr>
<tr><td>a increment_frog_twice
 i frog
 i frog
 d frog
p frog
increment_frog_twice
p frog</td><td></td></tr>
<tr><td>a increment_twice
 i 1
 i 1
 d 1 #never reached
a increment_both_twice
 increment_twice 1
 increment_twice 2
 d 1 #never reached
increment_both_twice platypus duck
p platypus
p duck</td><td></td></tr>
<tr><td>a set_to_zero
 d 1
 set_to_zero 1
 i _dummy_
i oryx
i oryx
i oryx
p oryx
set_to_zero oryx
p oryx</td><td></td></tr>
<tr><td>#Command Definitions:
a s             #flag as a success
 i _
 d _
 d _
a f             #flag as a failure
 d _
 d _
 d _
a z             #1 = zero
 d 1
 z 1
 s
a n             #1 = one
 z 1
 i 1
 s
a move          #2 += 1, 1 = zero
 moveH 1 2
 move 1 2
 s
a moveH         #move helper
 d 1
 i 2
 f
a dupe          #2 += 1, 3 += 1, 1 = zero
 dupeH1 1 2 3
 dupe 1 2 3
 s
a dupeH1        #dupe helper
 d 1
 dupeH2 2 3
 f
a dupeH2        #dupe helper
 i 1
 i 2
 s
a copy          #2 = 1
 z 2
 copyH 1 2
 s
a copyH         #copy helper
 dupe 1 2 _copy
 move _copy 1
 s
a addTo         #1 += 2
 copy 2 _add
 #testing comments #
 move _add 1#in weird places # just because #
 s
#it's a g##d idea
###
a add           #1 = 2 + 3
 #its a good idea
 z 1
 addH 1 2 3
 s
##

#
a addH          #add helper
#this is a comment
 addTo 1 2 #as is this
 addTo 1 3
 s
a mul           #1 = 2 * 3
 mulH1 1 2
 mulH2 1 3
 s
a mulH1         #mul helper
 z 1
 copy 2 _mul
 s
a mulH2         #mul helper
 mulH3 1 2
 mulH2 1 2
 s
a mulH3         #mul helper
 d _mul
 addTo 1 2
 f
a mulBy         #1 *= 2
 mul _mulBy 1 2
 copy _mulBy 1
 s
a pow           #1 = 2^3
 powH1 1 3
 powH2 1 2
 s
a powH1         #pow helper
 n 1
 copy 2 _pow
 s
a powH2         #pow helper
 powH3 1 2
 powH2 1 2
 s
a powH3         #pow helper
 d _pow
 mulBy 1 2
 f

#Running Tests:
p A
p B
p C
n A         #A = 1
n B         #B = 1
add C A B   #C = A + B = 1 + 1 = 2
p ____
p A
p B
p C
add B A C   #B = A + C = 1 + 2 = 3
p ____
p A
p B
p C
mul d B C   #d = B * C = 3 * 2 = 6
p ____
p d
mulBy d B   #d = d * B = 6 * 3 = 18
p ____
p d
d A         #A = A - 1 = 1 - 1 = 0
mulBy d A   #d = d * A = 18 * 0 = 0
p ____
p d
pow A C B   #A = C ^ B = 2 ^ 3 = 8
p ____
p A
p B
p C
pow A B C   #A = B ^ C = 3 ^ 2 = 9
p ____
p A
p B
p C
pow C A B   #C = A ^ B = 9 ^ 3 = 729
p ____
p A
p B
p C  
</td><td></td></tr>
</tbody>
</table>


Ich habe dem Testprogramm einige weitere Kommentare hinzugefügt und es sieht so aus, als ob sie dazu geführt haben, dass Ihr Code nicht funktioniert.
Calvins Hobbys

@ Calvin'sHobbies ein erster, schneller Patch
edc65

3

C # 6, 653 Bytes

Hier ist mein Eintrag, inmitten eines Meeres von Python ...

class P{string[]l;string r="";Dictionary<string,int>v=new Dictionary<string,int>();Dictionary<string,int>s=new Dictionary<string,int>();public string R(string t){l=t.Split('\n');for(int i=0;i<l.Length;i++){var z=l[i].Split(' ');if(z[0]=="a"){s.Add(z[1],i);i+=3;}else E(i, null);}return r;}bool E(int n,string[]p){var z=l[n].Split(' ');var a=z.Skip(1).Select(x=>Char.IsDigit(x[0])?p[int.Parse(x)-1]:x).ToArray();if(a.Length>0&&!v.ContainsKey(a[0]))v[a[0]]=0;if (z[0]=="p")r+=$"{a[0]} = {v[a[0]]}\n";else if(z[0]=="i")v[a[0]]++;else if(z[0]=="d")if(v[a[0]]>0)v[a[0]]--;else return false;else{var y=s[z[0]];return E(y+1,a)?E(y+2,a):E(y+3,a);}return true;}}

Erweitert und kommentiert:

class Prindeal
{
    string[] lines;
    string result = "";
    Dictionary<string, int> variables = new Dictionary<string, int>();
    Dictionary<string, int> statements = new Dictionary<string, int>();

    public string Run(string text)
    {
        lines = text.Split('\n');

        for (int i = 0; i < lines.Length; i++)
        {
            // Split on spaces to get the statement and any arguments
            var z = lines[i].Split(' ');

            // Are we defining a new statement?
            if (z[0] == "a")
            {
                // Add to the statements dictionary, step over definition statements
                statements.Add(z[1], i);
                i += 3;
            }
            else
            {
                // Execute the statement
                Execute(i, null);
            }
        }

        return result;
    }

    bool Execute(int lineNumber, string[] parameters)
    {
        // Split on spaces to get the statement and any arguments
        var z = lines[lineNumber].Split(' ');

        // Parse the arguments - if it's a number, get the corresponding 
        // parameter from the calling statement
        var arguments = z.Skip(1).Select(
            x => Char.IsDigit(x[0]) ? 
            parameters[int.Parse(x) - 1] : 
            x)
            .ToArray();

        // If the first argument isn't already in the variables dict, add it
        if (arguments.Length > 0 && !variables.ContainsKey(arguments[0])) variables[arguments[0]] = 0;

        // Print statement, using string interpolation
        if (z[0] == "p")
            result += $"{arguments[0]} = {variables[arguments[0]]}\n";
        // Increment statement
        else if (z[0] == "i")
            variables[arguments[0]]++;
        // Decrement statement
        else if (z[0] == "d")
            if (variables[arguments[0]] > 0)
                variables[arguments[0]]--;
            else
                return false;
        else
        {
            // Get the line number to jump to
            var y = statements[z[0]];

            // Execute A ? B : C
            return Execute(y + 1, arguments) ? Execute(y + 2, arguments) : Execute(y + 3, arguments);
        }

        // If we reach this point, it's from a 'p', 'i' or 'd' statement which has succeeded
        return true;
    }
}

Um es zu verwenden, instanziieren Sie einfach die Klasse und rufen Sie die R()Methode auf, zum Beispiel:

string prindealText = new StreamReader("prindeal.txt").ReadToEnd();
Console.WriteLine(new P().R(prindealText));

3

Gemeines Lisp, 758 646 619

(progn(set-macro-character #\#(get-macro-character #\;))(setf(readtable-case *readtable*):invert)(#3=defun v(s)(if(boundp s)(eval s)0))(#3# i(s)(set s(1+ (v s))))(#3# d(s)(and(plusp(v s))(set s(1-(v s)))))(#3# p(s)(format t"~A = ~A~%"s(v s)))(defmacro a(n . p)`(#3#,(cadr n)(&rest g)(if,@p)))(#3# k(s)(typecase s(integer`(nth,(1- s)g))(symbol `',s)(t(list*(car s)(mapcar 'k(cdr s))))))(#3# r()(prog(l p q)$(setf p()l(make-string-input-stream(or(read-line()()())(return))))@(when(setf p(read l()()))(push p q)(go @))(if q(return(k(reverse q)))(go $))))(do ((x(r)(r)))((not x))(eval(if(eq(car x)'a)`(,@x,(r),(r),(r))x))))

Geben Sie dies ein file.lispund rufen Sie zum Beispiel an sbcl --script file.lisp; Die Eingabe wird aus dem Standardeingabestream gelesen.

Diese Version analysiert eine Obermenge von Prindeal: Ohne große Schwierigkeiten können Sie von einer Prindeal-Quelle aus auf alle Common Lisp zugreifen. Ich halte dies für ein Merkmal des Interpreters.

Kommentierte Version

;; copy-readtable is only used during development, so that I do not 
;; mess with my running environment. The real code starts with the
;; progn below, which is superfluous of course inside a let.
(let ((*readtable* (copy-readtable)))

  ;; I use PROGN in the golfed version so that I can have the whole
  ;; program as a unique tree. This allows me to define reader 
  ;; variables like #3=defun in order to gain a few bytes by writing
  ;; #3# instead of defun. Reader variables are removed in
  ;; this human-friendly version.
  (progn
    ;; Let # point to the same reader function as ;
    ;; Of course, ; is still usable as a comment delimiter
    (set-macro-character #\#
                         (get-macro-character #\;))

    ;; :invert does what is necessary to enable case-sensitive reading
    ;; and printing of symbols
    (setf (readtable-case *readtable*) :invert)

    ;; value of symbol, or zero
    (defun v(s)(if(boundp s)(eval s)0))

    ;; increment
    (defun i(s)(set s(1+ (v s))))

    ;; decrement
    (defun d(s)(and(plusp(v s))(set s(1-(v s)))))

    ;; print
    (defun p(s)(format t"~A = ~A~%"s(v s)))

    ;; alias: wrap an "if" inside a "defun".
    ;; YES, that means you can redefine ANY lisp function with "a" !
    ;; A safer version would properly intern symbols in a dedicated package.
    ;;
    ;; Notice the G variable.  We take advantage of the "unhygienic"
    ;; (what a bad adjective) nature of macros to create a context
    ;; where G is bound to the argument list. The same G is referenced
    ;; implicitely later.
    (defmacro a(n . p)`(defun,(cadr n)(&rest g)(if,@p)))

    ;; Canonicalize expressions:
    ;;
    ;; - if s is a symbol, return s quoted. All functions manipulate
    ;; symbols in order to allow the undeclared use of variables. With
    ;; symbols, we can check for boundness.
    ;;
    ;; - if s is an integer, then we are inside an alias definition. The
    ;; integer is replaced by an access to the s'th element of the
    ;; implicit argument list G using (nth (1- s) g). G will be bound
    ;; when the expressions is injected in the defun corresponding to
    ;; the alias, or else an error will be signaled: either because G
    ;; is unbound, or because you defined a variable named G which is
    ;; by construction not a list. Since we do not sanitize properly
    ;; the input, you could bind G globally to a list, but that would be
    ;; nasty.
    ;; 
    ;; - Finally, if s is a list, apply k to all but the first
    ;; elements of s.  The first element is a symbol but we do not
    ;; need to quote it because we want to call the function
    ;; associated with the symbol. Due to the Lisp-2-ness
    ;; of Common Lisp, functions and variables can coexist
    ;; with the same name.
    ;;
    (defun k(s)(typecase s
                 (integer`(nth,(1- s)g))
                 (symbol`',s)
                 (t(list*(car s)(mapcar #'k(cdr s))))))

    ;; Reader function
    (defun r()
      (prog (l ; current line, as an input-stream reading a string
             p ; current read form
             q ; whole line and return value, as a list
             )

         ;; PROG includes an implicit TAGBODY. Below, $ and @ are
         ;; labels for GO statements (gotos).

       $ (setf
          ;; emtpy p
          p ()

          ;; Read a whole line and if we do not fail, build an input
          ;; stream to read from it.
          l (make-string-input-stream
             (or (read-line()()()) ;; try to read a line,
                 (return)          ;; but return from prog if we reach
                                   ;; the end of file.
                 )))
       @ (when (setf p (read l()()))
           ;; Read a lisp expression, put it in p and if p is not nil
           ;; push it into q.  A nil could happen at the end of the
           ;; line or if someone (you know who) inserted an empty list
           ;; in the file being read.
           ;; 
           ;; Thanks to the readtable which now handles comments
           ;; and spaces for us, nothing needs to be done here to
           ;; preprocess the input.

           (push p q) (go @))

         ;; If we read an empty line, q can be nil. In this case, go
         ;; back to $ and read another line. If q is not nil, reverse
         ;; it (we pushed, remember), canonicalize it and return the
         ;; result.
         (if q (return(k(reverse q))) (go $)))
      )

    ;; Read/eval loop.  When reading "(a name)", we read the three
    ;; next lines and append them to the first so that it builds a
    ;; call the the alias definition macro a. Otherwise, just eval x.
    (do((x(r)(r))((not x))
      (eval (if (eq(car x'a))
                `(,@x,(r),(r),(r))
                x)))))

Beispiel

~$ sbcl --script file.lisp < testfile

A = 0
B = 0
C = 0
____ = 0
A = 1
B = 1
C = 2
____ = 0
A = 1
B = 3
C = 2
____ = 0
d = 6
____ = 0
d = 18
____ = 0
d = 0
____ = 0
A = 8
B = 3
C = 2
____ = 0
A = 9
B = 3
C = 2
____ = 0
A = 9
B = 3
C = 729

Wenn wir in der Lese- / Bewertungsschleife evaldurch ersetzen print, können wir sehen, was ausgewertet wird:

(a 's (i '_) (d '_) (d '_)) 
(a 'f (d '_) (d '_) (d '_)) 
(a 'z (d (nth 0 g)) (z (nth 0 g)) (s)) 
(a 'n (z (nth 0 g)) (i (nth 0 g)) (s)) 
(a 'move (moveH (nth 0 g) (nth 1 g)) (move (nth 0 g) (nth 1 g)) (s)) 
(a 'moveH (d (nth 0 g)) (i (nth 1 g)) (f)) 
(a 'dupe (dupeH1 (nth 0 g) (nth 1 g) (nth 2 g))
   (dupe (nth 0 g) (nth 1 g) (nth 2 g)) (s)) 
(a 'dupeH1 (d (nth 0 g)) (dupeH2 (nth 1 g) (nth 2 g)) (f)) 
(a 'dupeH2 (i (nth 0 g)) (i (nth 1 g)) (s)) 
(a 'copy (z (nth 1 g)) (copyH (nth 0 g) (nth 1 g)) (s)) 
(a 'copyH (dupe (nth 0 g) (nth 1 g) '_copy) (move '_copy (nth 0 g)) (s)) 
(a 'addTo (copy (nth 1 g) '_add) (move '_add (nth 0 g)) (s)) 
(a 'add (z (nth 0 g)) (addH (nth 0 g) (nth 1 g) (nth 2 g)) (s)) 
(a 'addH (addTo (nth 0 g) (nth 1 g)) (addTo (nth 0 g) (nth 2 g)) (s)) 
(a 'mul (mulH1 (nth 0 g) (nth 1 g)) (mulH2 (nth 0 g) (nth 2 g)) (s)) 
(a 'mulH1 (z (nth 0 g)) (copy (nth 1 g) '_mul) (s)) 
(a 'mulH2 (mulH3 (nth 0 g) (nth 1 g)) (mulH2 (nth 0 g) (nth 1 g)) (s)) 
(a 'mulH3 (d '_mul) (addTo (nth 0 g) (nth 1 g)) (f)) 
(a 'mulBy (mul '_mulBy (nth 0 g) (nth 1 g)) (copy '_mulBy (nth 0 g)) (s)) 
(a 'pow (powH1 (nth 0 g) (nth 2 g)) (powH2 (nth 0 g) (nth 1 g)) (s)) 
(a 'powH1 (n (nth 0 g)) (copy (nth 1 g) '_pow) (s)) 
(a 'powH2 (powH3 (nth 0 g) (nth 1 g)) (powH2 (nth 0 g) (nth 1 g)) (s)) 
(a 'powH3 (d '_pow) (mulBy (nth 0 g) (nth 1 g)) (f)) 
(p 'A) 
(p 'B) 
(p 'C) 
(n 'A) 
(n 'B) 
(add 'C 'A 'B) 
(p '____) 
(p 'A) 
(p 'B) 
(p 'C) 
(add 'B 'A 'C) 
(p '____) 
(p 'A) 
(p 'B) 
(p 'C) 
(mul 'd 'B 'C) 
(p '____) 
(p 'd) 
(mulBy 'd 'B) 
(p '____) 
(p 'd) 
(d 'A) 
(mulBy 'd 'A) 
(p '____) 
(p 'd) 
(pow 'A 'C 'B) 
(p '____) 
(p 'A) 
(p 'B) 
(p 'C) 
(pow 'A 'B 'C) 
(p '____) 
(p 'A) 
(p 'B) 
(p 'C) 
(pow 'C 'A 'B) 
(p '____) 
(p 'A) 
(p 'B) 
(p 'C)

Makroexpansion

Wenn wir die folgende Aliasdefinition auswählen:

(a 'powH2 (powH3 (nth 0 g) (nth 1 g)) (powH2 (nth 0 g) (nth 1 g)) (s))

... wir können Verweise auf eine Variable mit dem Namen sehen, gdie im lexikalischen Bereich nirgends zu finden ist. Aber nach der Makroexpansion wird hier der eigentliche Code ausgewertet:

(defun powH2 (&rest g)
  (if (powH3 (nth 0 g) (nth 1 g))
      (powH2 (nth 0 g) (nth 1 g))
      (s))) 

Nun gbezieht sich auf die Argumentliste der Funktion definiert ist.


2

Python 2, 486 Bytes

Dies ist die Referenzlösung, mit der ich mehr Golf gespielt habe (derzeit -98 Bytes).

import sys;sys.setrecursionlimit(2000)
def r(s):
 n=s[0]
 if n in A:f=lambda i:r([s[int(t)]if'0'<t[0]<':'else t for t in A[n][i]]);return f(1+(f(0)or 0))
 k=s[1]
 if'i'<n:print k,'=',V.get(k,0)
 elif'd'<n:V[k]=-~V[k]if k in V else 1
 elif'a'<n:
    if~-(k in V)or V[k]<1:return 1
    V[k]-=1
 else:A[k]=s[2:]
A={};V={};c=filter(bool,([l,l[:l.find('#')]]['#'in l]for l in input().split('\n')))
while c:
 s=c[0].split();c=c[1:]
 if'a'!=s[0]:r(s)
 else:r(['a',s[1]]+map(str.split,c[:3]));c=c[3:]

Änderungen (an die ich mich erinnere):

  • automatische Boolean-Integer-Konvertierung ( [l,l[:l.find('#')]]['#'in l]).
  • setze oder inkrementiere in einer Anweisung ( V[k]=-~V[k]if k in V else 1)
  • mehr Aliase zu längeren Ausdrücken ( k=s[1])
  • Kein Zähler in der Hauptschleife, stattdessen wird die Eingabeliste gelöscht
  • printLeerzeichen automatisch hinzufügen ( print k,'=',V.get(k,0))
  • Ziffern 1-9 prüfen ( '0'<t[0]<':')
  • Kippen Sie die Rückgabewerte von rum, um returns zu sparen
  • Wiederholung des Schneidens und Aufteilens entfernen ( map(str.split,c[:3])))

1

Python 3, 1322 Bytes

Golf gespielt:

import re,sys;sys.setrecursionlimit(2000);F,L=filter,list
class P:
 N,O,F=0,{},{}
 def __init__(S,c):
  S.B,S.E={"p":S.P,"i":S.I,"d":S.D,"a":S.L},dict(enumerate(F(None,[i.split('#')[0].rstrip()for i in c.splitlines()])))
  while S.N in S.E:S.X(S.E[S.N])
 def V(S, v, y, z=0):
  if re.match("[\w_][\d\w_]*",v):
   if not v in y:
    if z is not None:y[v]=z
    else:return False
   return True
  return False
 def A(S):S.N+=1
 def P(S,v):
  if S.V(v,S.O):print("{0} = {1}".format(v, S.O[v]));return True
  return False
 def I(S,v):
  if S.V(v, S.O):S.O[v]+=1;return True
  return False
 def D(S,v):
  if S.V(v,S.O)and S.O[v]>0:S.O[v]-=1;return True
  return False
 def L(S,v):
  e=[]
  if S.V(v,S.F,e):
   for i in range(3):S.A();e.append(S.E[S.N].lstrip())
   return True
  return False
 def C(S,c,v):
  def R(Z,v):
   for i in re.findall("\s(\d+)", Z):Z=Z.replace(" %s"%i," %s"%v[int(i)-1])
   return Z
  Q,m,f=map(lambda l:R(l,v),S.F[c])
  if S.X(Q,False):return S.X(m,False)
  return S.X(f,False)
 def X(S,Z,C=True):
  u=re.match("\s?([\w_][\d\w_]*)\s?([\w_][\d\w ]*)?",Z)
  if u:
   c,v=map(lambda i:''if i is None else i,u.groups());v=L(F(None,v.split(' ')))
   if S.V(c,S.F,None):
    T=S.C(c, v)
    if C:S.A()
   elif S.V(c,S.B,None):
    T=S.B[c](*v)
    if C:S.A()
   else:return False
   return T
  return False

Ungolfed:

import re

class Prindeal:
    iline = 0
    local = {}
    udef = {}
    content  = {}

    def __init__(self, c):
        self.built = {
            "p": self.print,
            "i": self.increment,
            "d": self.decrement,
            "a": self.alias,
        }
        self.content = dict(enumerate(filter(None, [i.split('#')[0].rstrip()for i in c.splitlines()])))
        while self.iline in self.content:
            self.execute_line(self.content[self.iline])

    def validate_name(self, varname, stack, default=0):
        if re.match("[\w_][\d\w_]*", varname):
            if not varname in stack:
                if default is not None:
                    stack[varname] = default
                else:
                    return False
            return True
        return False

    def advance_stack(self):
        self.iline += 1

    def print(self, varname):
        if self.validate_name(varname, self.local):
            print("{0} = {1}".format(varname, self.local[varname]))
            return True
        return False

    def increment(self, varname):
        if self.validate_name(varname, self.local):
            self.local[varname] += 1
            return True
        return False

    def decrement(self, varname):
        if self.validate_name(varname, self.local) and self.local[varname] > 0:
            self.local[varname] -= 1
            return True
        return False

    def alias(self, aliasname):
        indexed_lines = []
        if self.validate_name(aliasname, self.udef, indexed_lines):
            for i in range(3):
                self.advance_stack()
                indexed_lines.append(self.content[self.iline].lstrip())
            return True
        return False

    def execute_alias(self, cmd, variables):
        def parse_args(line, variables):
            for i in re.findall("\s(\d+)", line):
                line = line.replace(" %s" % i, " %s" % variables[int(i) - 1])
            return line
        init, success, failure = map(lambda l: parse_args(l, variables), self.udef[cmd])
        if self.execute_line(init, False):
            return self.execute_line(success, False)
        return self.execute_line(failure, False)

    def execute_line(self, line, cont=True):
        valid_execution = re.match("\s?([\w_][\d\w_]*)\s?([\w_][\d\w ]*)?", line)
        if valid_execution:
            cmd, variables = map(lambda i: '' if i is None else i, valid_execution.groups())
            variables = list(filter(None, variables.split(' ')))
            if self.validate_name(cmd, self.udef, None):
                temp = self.execute_alias(cmd, variables)
                if cont:
                    self.advance_stack()
            elif self.validate_name(cmd, self.built, None):
                temp = self.built[cmd](*variables)
                if cont:
                    self.advance_stack()
            else:
                return False
            return temp
        return False

Verwendung:

P(c)

Wo cist der Textinhalt.

Beispiele:

Einzeilige Zeichenfolgen werden akzeptiert:

  • P("p cat")
  • P("p dog\ni dog\np dog")

Mehrzeilige Zeichenfolgen werden ebenfalls akzeptiert:

P("""
p dog
i dog
p dog
""")

Oder:

P("""p dog
i dog
p dog""")

Etc.

Anmerkungen:

Dies funktioniert ordnungsgemäß für alle Testfälle, erreicht jedoch die Rekursionsgrenze für:

pow C A B   #C = A ^ B = 9 ^ 3 = 729

Daher die sys.setrecursionlimit(2000).


1
Es wird einige Bytes verbrauchen, aber könnten Sie nicht sys.setrecursionlimit () verwenden, um dies mit dem Pow-Alias ​​richtig zu machen?
Corwin

Ich könnte, aber OP erklärte, dass Sprachen wie Python (die Rekursionsgrenzen haben) so wie sie sind akzeptiert werden. Ich werde das Update jedoch hinzufügen, wenn dies von OP angefordert wird. @Corwin
Zach Gates

Meinetwegen. Habe das in der Spezifikation verpasst. @ZachGates
Corwin

1

Python - 695 688 Bytes

def p(v):print v,"=",w.get(v,0)
def i(v):w[v]=w.get(v,0)+1
def d(v):
 if v in w:
<TAB>w[v]-=1
<TAB>if not w[v]:del w[v]
 else:return 1
def a(n,b,d,h):
 def g(*a):
<TAB>i=1;f=b;s=d;t=h
<TAB>for v in a:v=q+v+q;k=q+j(i)+q;f=c(f,k,v);s=c(s,k,v);t=c(t,k,v);i+=1
<TAB>y=u(t,e)if u(f,e)else u(s,e);i=1;return y
 e[n]=g
q="'";w=x={};u=eval;e={'a':a,'d':d,'i':i,'p':p};import sys;l=sys.stdin.readlines();r="";j=str;c=j.replace;sys.setrecursionlimit(2000)
for h in l:
 h = h.strip()
 if not h:continue
 l = h.split();f=l[0];n=f+"("
 if "#" in f:continue
 for g in l[1:]:
<TAB>b=g.find("#")+1
<TAB>if b:g=g[:b-1]
<TAB>if g:n+="'%s',"%g
<TAB>if b:break
 if x:x-=1;d+='"%s)",'%n
 else:x=(f=="a")*3;d=n
 if not x:d+=")\n";r+=d
exec r in e

<TAB> ist ein wörtliches Tabulatorzeichen.


1

C ++, 1111 Bytes

Dieser ist C ++ - so idiomatisch wie ich es machen könnte.
Das bedeutet, es mehr C ++ - und weniger C-ish zu machen.
Das bedeutet auch, dass es größer als das entsprechende C-Programm ist.
Ich denke, C ++ konkurriert mit Java um eine ausführliche Standardbibliothek.
Kompiliert mit VS2013 und g ++ 4.9.2 (mit -std = c ++ 11)

#include<array>
#include<iostream>
#include<map>
#include<regex>
#include<sstream>
#include<stack>
#define B std::
#define a first
#define b second
#define c(s);else if(x.a==s)
#define d(n)B getline(B cin,r##n)
#define e(n)r##n=B regex_replace(r##n,q,"$1");
#define f(n)do{d(n);e(n)}while(r##n.empty());
#define g B string
#define h B istream_iterator<g>
#define i p.top().a
#define j p.empty()
#define k B pair
#define u continue;
#define w B back_inserter
typedef B vector<g>s;typedef B array<g,3>A;typedef k<k<long,A>,s>t;B map<g,A>m;B map<g,long>n;B stack<t>p;B regex q("^ *(.*?) *(#.*)?$");int main(){g r0,r1,r2,r3;while(d(0)){e(0)if(r0.empty())u p.push(t{{0,{{r0,"",""}}},{}});bool z;while(!j){k<g,s>x;B istringstream ss(i.b[i.a]);ss>>x.a;B copy(h(ss),h(),w(x.b));s o;B transform(B begin(x.b),B end(x.b),w(o),[](g y){int v=atoi(y.c_str());return v>0?p.top().b[v-1]:y;});z=true;if(0)c("")c("p")B cout<<o[0]<<" = "<<n[o[0]]<<B endl c("i")n[o[0]]++c("d")n[o[0]]-=(z=n[o[0]])c("a"){f(1)f(2)f(3)m.insert(B make_pair(o[0],A{{r1,r2,r3}}));}else{p.push(t{{0,m[x.a]},o});u}while(!j&&i.a)p.pop();if(!j)i.a+=1+!z;}}}

Unten ist das Original. Wenn jemand eine Möglichkeit finden sollte, es gleichzeitig idiomatischer und kürzer zu gestalten, lassen Sie es mich bitte wissen.

#include <array>
#include <iostream>
#include <map>
#include <regex>
#include <sstream>
#include <stack>

typedef std::vector<std::string> List;
typedef std::pair<std::string, List> Statement;
typedef std::array<std::string, 3> Alias;
typedef std::pair<long, Alias> IndexedAlias;
typedef std::pair<IndexedAlias, List> Item;

std::map<std::string, Alias> aliases;
std::map<std::string, long> variables;
std::stack<Item> stack;
std::regex re("^ *(.*?) *(#.*)?$");

int main()
{
    std::string line, line1, line2, line3;
    while (std::getline(std::cin, line)) // control-Z to exit
    {
        line = std::regex_replace(line, re, "$1");
        if (line.empty()) continue;
        stack.push(Item{ { 0, { { line, "", "" } } }, {} });

        bool flag;
        while (!stack.empty())
        {
            Statement statement;
            std::istringstream ss(stack.top().first.second[stack.top().first.first]);
            ss >> statement.first;
            std::copy(std::istream_iterator<std::string>(ss), std::istream_iterator<std::string>(), std::back_inserter(statement.second));

            List arguments;
            std::transform(std::begin(statement.second), std::end(statement.second), std::back_inserter(arguments),
                [](std::string arg){ int i = atoi(arg.c_str()); return i > 0 ? stack.top().second[i - 1] : arg; });

            flag = true;
            if (statement.first == "")
                ;
            else if (statement.first == "p")
                std::cout << arguments[0] << " = " << variables[arguments[0]] << std::endl;
            else if (statement.first == "i")
                variables[arguments[0]]++;
            else if (statement.first == "d")
                variables[arguments[0]] -= (flag = variables[arguments[0]]);
            else if (statement.first == "a")
            {
                do { std::getline(std::cin, line1); line1 = std::regex_replace(line1, re, "$1"); } while (line1.empty());
                do { std::getline(std::cin, line2); line2 = std::regex_replace(line2, re, "$1"); } while (line2.empty());
                do { std::getline(std::cin, line3); line3 = std::regex_replace(line3, re, "$1"); } while (line3.empty());
                aliases.insert(std::make_pair(arguments[0], Alias{ { line1, line2, line3 } }));
            }
            else
            {
                stack.push(Item{ { 0, aliases[statement.first] }, arguments });
                continue;
            }

            while (!stack.empty() && stack.top().first.first) stack.pop();
            if (!stack.empty()) stack.top().first.first += 1 + !flag;
        }
    }

    std::cout << "-- Variables --" << std::endl;
    std::transform(std::begin(variables), std::end(variables), std::ostream_iterator<std::string>(std::cout, "\n"),
        [](std::map<std::string, long>::value_type pair){ std::ostringstream ss; ss << pair.first << " = " << pair.second; return ss.str(); });
    std::cout << "-- Aliases --" << std::endl;
    std::transform(std::begin(aliases), std::end(aliases), std::ostream_iterator<std::string>(std::cout, "\n"),
        [](std::map<std::string, Alias>::value_type pair){ std::ostringstream ss; ss << pair.first << " = [1]:" << pair.second[0] << " [2]:" << pair.second[1] << " [3]:" << pair.second[1]; return ss.str(); });
    std::cout << "---------------" << std::endl;

    return 0;
}

0

Haskell, 1009

Ich habe mein Bestes gegeben, um Golf zu spielen. Mein ungolfed Code bestand aus über 3.000 Zeichen. An diesem Punkt kann ich mich nicht erinnern, was alle Funktionen tun, wenn ich Golf spiele, heißt das erraten, was es kaputt macht und was nicht.

import qualified Data.Map as M
import Control.Monad.State.Lazy
import Data.List
type A=M.Map String
data P=P(A Int)(A([String]->StateT P IO Int))
a f=evalStateT f(P M.empty$M.fromList[("i",\(b:_)->(+1)%b),("d",\(b:_)->pred%b),("p",\(b:_)->i b>>= \v->liftIO(putStrLn$b++"="++show v)>>q 1)])
e(k:l)=do{(P v a)<-get;put.P v$M.insert k(m l)a;q 1}
g t s f= \a->t a>>= \b->if b>0then s a else f a
f%k=f<$>i k>>= \v->if v<0then k#0>>q 0else k#v>>q 1
i k=get>>= \(P v _)->q$M.findWithDefault 0 k v
k#v=get>>= \(P b a)->put$P(M.insert k v b)a
l k=get>>= \(P _ a)->q$a M.!k
f s=let(f:a)=r s in($a)<$>l f>>=id
m(t:s:f:_)=g(k t)(k s)(k f)
k s=let(f:b)=r s in\a->($(map((\y z->if all(\c->c>'/'&&c<':')z then y!!(read z-1)else z)a)b))<$>l f>>=id
n=dropWhileEnd(==' ').takeWhile(not.(=='#')).dropWhile(==' ')
o[]=[]
o(l:ls)|(head.r$l)=="a"=(l:take 3 ls):(o$drop 3 ls)|1>0=[l]:o ls
p s|length s>1=e$(n.tail.head$s):tail s|1>0=f.head$s
q=return
main=join$a.(\s->mapM_ p(o.filter(not.null).map n.lines$s))<$>getContents
r=words
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.