Implementiere Anyfix Notation!

16

In der Präfixnotation steht der Operator vor den Argumenten, sodass Sie sich vorstellen können, dass der Operator next()rekursiv aufruft . In der Infixnotation bewegt sich der Operator zwischen den Argumenten, sodass Sie es sich einfach als Analysebaum vorstellen können. In der Postfix-Notation folgt der Operator den Argumenten, sodass Sie es sich einfach als stapelbasiert vorstellen können.

In Anyfix-Notation kann der Operator überall hingehen * . Wenn ein Operator angezeigt wird und nicht genügend Argumente vorhanden sind, wartet der Operator, bis genügend Argumente vorhanden sind. Für diese Herausforderung müssen Sie einen sehr einfachen Anyfix-Evaluator implementieren. (Beachten Sie, dass anyfix eine von mir aufgegebene Freizeitsprache ist, mit der Sie hier herumspielen oder hier nachsehen können. )

Sie müssen die folgenden Befehle unterstützen:

(Teil 1)

  • Duplikat
  • Negativ

(Teil 2)

  • Zusatz
  • Multiplikation
  • Gleichheit: kehrt zurück 0oder 1.

Sie können für diese Befehle fünf beliebige Symbole ohne Leerzeichen verwenden. Zu Demonstrationszwecken verwende ich "als Duplikat, ×als Multiplikation und +als Addition.

Für Literale müssen Sie nur nicht negative Ganzzahlen unterstützen, Ihr Interpreter muss jedoch in der Lage sein, alle Ganzzahlen (innerhalb des (angemessenen) Ganzzahlbereichs Ihrer Sprache) zu enthalten.

Lassen Sie uns ein Beispiel nehmen einen Blick: 10+5. Der Speicher sollte sich wie ein Stapel und nicht wie eine Warteschlange verhalten. Zuerst beginnt der Stapel um []und die Liste der in der Warteschlange befindlichen Operatoren beginnt um []. Dann wird das Literal 10ausgewertet, wodurch der Stapel entsteht [10]. Als nächstes wird der Operator +ausgewertet, für den zwei Argumente erforderlich sind. Es gibt jedoch nur ein Argument auf dem Stapel, sodass die Liste der in die Warteschlange gestellten Operatoren wird ['+']. Dann wird das Literal 5ausgewertet, wodurch der Stapel entsteht [10, 5]. Zu diesem Zeitpunkt kann der Operator '+'so ausgewertet werden, dass er den Stapel [15]und die Warteschlange erstellt [].

Das Endergebnis sollte [15]für + 10 5, 10 + 5und 10 5 +.

Lassen Sie sich ein schweres Beispiel einen Blick: 10+". Der Stapel und die Warteschlange beginnen mit []und []. 10wird zuerst ausgewertet, wodurch der Stapel entsteht [10]. Als nächstes +wird ausgewertet, was den Stack nicht ändert (weil es nicht genügend Argumente gibt) und die Queue macht ['+']. Dann "wird ausgewertet. Dies kann sofort so laufen, wie es ist, und macht den Stapel [10, 10]. +Jetzt kann ausgewertet werden, so dass der Stack [20]und die Warteschlange werden []. Das Endergebnis ist [20].

Was ist mit der Reihenfolge der Operationen?

Lassen Sie uns einen Blick darauf werfen ×+"10 10. Der Stapel und die Warteschlange beginnen wie folgt []:

  • ×: Der Stapel ist unverändert und die Warteschlange wird ['×'].
  • +: Der Stapel ist unverändert und die Warteschlange wird ['×', '+'].
  • ": Der Stapel ist unverändert und die Warteschlange wird ['×', '+', '"'].
  • 10: Der Stapel wird [10]. Auch wenn ×der erste Operator, der ausgewertet werden soll, da er zuerst angezeigt wird, "sofort ausgeführt werden kann und keiner der Operatoren zuvor kann, wird er ausgewertet. Der Stapel wird [10, 10]und die Warteschlange ['×', '+']. ×Jetzt kann ausgewertet werden, was den Stack [100]und die Queue macht ['+'].
  • 10: Der Stack wird [100, 10], der +ausgewertet werden kann. Der Stapel wird [110]und die Warteschlange [].

Das Endergebnis ist [110].

Die in diesen Demonstrationen verwendeten Befehle stimmen mit denen der anyfix-Sprache überein. Das letzte Beispiel funktioniert jedoch aufgrund eines Fehlers in meinem Interpreter nicht. (Haftungsausschluss: Ihre Beiträge werden nicht im anyfix-Interpreter verwendet.)

Herausforderung

Wählen Sie einen Satz von 5 Nicht-Whitespace-Zeichen und erstellen Sie einen Anyfix-Interpreter gemäß den obigen Spezifikationen. Ihr Programm kann entweder das einzelne Array oder den darin enthaltenen Wert ausgeben. Es wird garantiert, dass der Stapel von Werten am Ende der Ausführung nur einen einzigen Wert enthält und dass die Warteschlange der Operatoren am Ende der Ausführung leer ist.

Das ist also gewinnt der kürzeste Code in Bytes.

Testfälle

Für diese Testfälle ist Duplikat ", Negativ -, Addition +, Multiplikation ×und Gleichheit =.

Input -> Output
1+2×3 -> 9
1"+"+ -> 4
2"××" -> 16
3"×+" -> 18
3"+×" -> 36
123"= -> 1 ("= always gives 1)
1+2=3 -> 1
1"=2+ -> 3
1-2-+ -> -3
-1-2+ -> 3 (hehe, the `-1` becomes `+1` at the `-` rather than making the `2` a `-1`)
+×"10 10 -> 200 (after the 10 is duplicated (duplication is delayed), 10 + 10 is performed and then 20 * 10, giving 200)

Regeln

  • Es gelten Standardlücken
  • Sie können den offiziellen Anyfix-Dolmetscher nehmen und Golf spielen, wenn Sie möchten. Erwarten Sie, schrecklich zu verlieren.

Die Eingabe wird als Zeichenfolge und die Ausgabe als Array (eine einzelne Ganzzahl) aus der Zeichenfolgendarstellung von beiden angegeben. Sie können davon ausgehen, dass die Eingabe nur Leerzeichen, Ziffern und die von Ihnen gewählten 5 Zeichen enthält.

* nicht wirklich

HyperNeutrino
quelle
2
Gehen Sie überall hin * ™.
Jonathan Allan
Was ist das Ergebnis des Gleichstellungsoperators? 0und 1?
Felix Palmen
1
@ JonathanAllan siehe oben; Ich habe ein Kommando entfernt
HyperNeutrino
1
@ RickHitchcock Sicher.
HyperNeutrino
1
Sie sollten wahrscheinlich ×+"10 10in die Testfälle oder andere Beispiele einbeziehen, die 1) die Verwendung eines Leerzeichens und 2) die Verwendung des doppelten Operators verzögern (zwei Dinge, die ich völlig übersehen habe).
Arnauld

Antworten:

5

JavaScript (ES6), 204 203 200 Bytes

Gibt eine ganze Zahl zurück.

e=>e.replace(/\d+|\S/g,v=>{for((1/v?s:v>','?u:b)[U='unshift'](v);!!u[0]/s[0]?s[U](u.pop()>'c'?s[0]:-S()):!!b[0]/s[1]?s[U](+eval(S(o=b.pop())+(o<'$'?'==':o)+S())):0;);},s=[],u=[],b=[],S=_=>s.shift())|s

Zeichen verwendet:

  • +: zusätzlich
  • *: Multiplikation
  • #: Gleichberechtigung
  • d: duplizieren
  • -: negativ

Testfälle

Formatiert und kommentiert

e => e.replace(                     // given an expression e, for each value v matching
  /\d+|\S/g, v => {                 // a group of digits or any other non-whitespace char.
    for(                            //   this loop processes as many operators as possible
      (                             //   insert v at the beginning of the relevant stack:
        1 / v ? s : v > ',' ? u : b //     either value, unary operator or binary operator
      )[U = 'unshift'](v);          //     (s[], u[] or b[] respectively)
      !!u[0] / s[0] ?               //   if there's at least 1 value and 1 unary operator:
        s[U](                       //     unshift into s[]:
          u.pop() > 'c' ?           //       for the duplicate operator:
            s[0]                    //         a copy of the last value
          :                         //       else, for the negative operator:
            -S()                    //         the opposite of the last value
        ) :                         //     end of unshift()
      !!b[0] / s[1] ?               //   if there's at least 2 values and 1 binary operator:
        s[U](                       //     unshift into s[]:
          +eval(                    //       the result of the following expression:
            S(o = b.pop()) +        //         the last value, followed by the
            (o < '$' ? '==' : o) +  //         binary operator o with '#' replaced by '=='
            S()                     //         followed by the penultimate value
          )                         //       end of eval()
        ) : 0;                      //     end of unshift()
    );                              //   end of for()
  },                                // end of replace() callback
  s = [],                           // initialize the value stack s[]
  u = [],                           // initialize the unary operator stack u[]
  b = [],                           // initialize the binary operator stack b[]
  S = _ => s.shift()                // S = helper function to shift from s[]
) | s                               // return the final result
Arnauld
quelle
Ich glaube nicht, dass das funktioniert -1+-2. Gibt 3 statt -3 zurück.
Rick Hitchcock
1
@ RickHitchcock Mein Verständnis ist, dass die 2. -sofort angewendet werden muss -1.
Arnauld
Ich würde mir vorstellen, dass das 2. -mit dem gehen würde, 2da es nach einem anderen Betreiber kommt. Vielleicht kann @HyperNeutrino klären. Der negative Operator kann in einigen Situationen mehrdeutig sein.
Rick Hitchcock
3

JavaScript (ES6), 162 152 143 150 Bytes

(s,N=s.replace(/(-?\d+)-|-(-)/g,'- $1 ').match(/(- ?)*?\d+|R/g))=>+eval(`R=${N[0]>'9'?N[1]:N[0]},${s.match(/[+*=]/g).map((o,i)=>'R=R'+o+'='+N[i+1])}`)

Leicht ungolfed:

(s,
 N=s.replace(/(-?\d+)-|-(-)/g,'- $1 ').      //change postfix negatives to prefix,
                                             //and change "--" to "- - "
     match(/(- ?)*?\d+|R/g)                  //grab numbers and duplicates
)=>+eval(`R=${N[0] > '9' ?  N[1] : N[0]},    //handle a delayed duplicate
          ${s.match(/[+*=]/g).               //grab the operators
              map((o,i)=>'R=R'+o+'='+N[i+1]) //create a comma-separated list of assignments
           }
         `)

Erläuterung

Ich benutze *für die Multiplikation und Rfür das Duplizieren. Die anderen Operatoren sind die gleichen wie in der Frage.

N wird zum Array der Zahlen (einschließlich der Duplikate).

Das replacebehandelt den Fall, in dem das negative Vorzeichen nach der Zahl steht. Zum Beispiel ändert es sich 1-zu - 1und -1-zu - -1.

Innerhalb der eval, s.matchschafft die Anordnung von binären Operatoren. Beachten Sie, dass dies immer ein Element weniger als hat N.

Das Ergebnis der Funktion ist evaldie Abbildung der Zahlen und Operatoren.

Hier ist, was evalfür jeden der Testfälle bearbeitet wird:

0+2*3        R=0,R=R+=2,R=R*=3        = 6 
1+2*3        R=1,R=R+=2,R=R*=3        = 9 
1R+R+        R=1,R=R+=R,R=R+=R        = 4 
2R**R        R=2,R=R*=R,R=R*=R        = 16 
3R*+R        R=3,R=R*=R,R=R+=R        = 18 
3R+*R        R=3,R=R+=R,R=R*=R        = 36 
123R=        R=123,R=R==R             = 1 
1+2=3        R=1,R=R+=2,R=R==3        = 1 
1R=2+        R=1,R=R==R,R=R+=2        = 3 
1-2-+        R=- 1,R=R+=- 2           = -3 
-1-2+        R=1,R=R+=2               = 3 
*+R10 10     R=10,R=R*=10,R=R+=10     = 110 
+*R10 10     R=10,R=R+=10,R=R*=10     = 200 
-1+--2       R=-1,R=R+=- -2           = 1 
-1+-2        R=-1,R=R+=-2             = -3 

Der Komma-Operator in einem JavaScript-Ausdruck gibt das Ergebnis seines letzten Ausdrucks zurück, sodass mapautomatisch ein verwendbarer Ausdruck zurückgegeben wird.

Das +Vorzeichen wird vor dem evalWechsel truezu 1und falsenach benötigt 0.

Die Verwendung Rsowohl des Variablen- als auch des Duplikat-Operators vereinfacht die mapUnterausdrücke des Operators erheblich .

Testfälle:

Rick Hitchcock
quelle
2
Ich denke nicht, dass die replaceArbeiten wie beabsichtigt sind. Dies kehrt 3für -1+--2und ich denke, 1wäre korrekt (die 1Änderungen unterschreiben dreimal, bevor es ein zweites Argument für die +verfügbaren gibt, was dazu führt -1 + 2).
Felix Palmen
Großartiger Fang, @FelixPalmen. Jetzt behoben.
Rick Hitchcock
2

JavaScript, 321 311 Bytes

_="f=a=>(a.replace(/\\d+|./g,mq!~(b='+*=\"- '.indexOf(a))|b>2j3j4j5&&o+aw1/y?(y*=-1wcz:1/y?oywcz:hz,rql.map(z=>m(lki,1)[0],i)),hq1/s[1]?sk0,2,+eval(`y${a=='='?a+a:a}s[1]`)):cz,cqlki,0,a),s=[],l=[],e='splice'),s)z(a,i)ys[0]w)^r``:q=z=>os.unshift(k[e](j?b-";for(i of"jkoqwyz")with(_.split(i))_=join(pop());eval(_)

Probieren Sie es online!

Die fünf Zeichen sind die gleichen wie in der Frage, mit Ausnahme der *Multiplikation.
Das Skript wird mit RegPack komprimiert . Das ursprüngliche Skript wird _nach der Auswertung in der Variablen gespeichert .


quelle
Ich glaube nicht, dass das funktioniert -1+-2. Gibt 3 statt -3 zurück.
Rick Hitchcock
@ RickHitchcock. Warum glauben Sie , sollte es zurückgeben -3statt 3?
Ich kann den negativen Operator falsch verstehen. Im Allgemeinen -1 + -2ist -3, aber sollte es als --1 + 2stattdessen analysiert werden?
Rick Hitchcock
1
@ RickHitchcock. Ich bin mir ziemlich sicher, dass das Ergebnis ist 3. Bevor es 2überhaupt auf den Stack geht, wird der zweite -ausgewertet, und deshalb haben wir 1 2 +den ja 3. Ah, und wahrscheinlich müssen Sie auch Ihre Antwort bearbeiten.
Du hast wahrscheinlich Recht. Sie und Arnauld erhalten die gleiche Antwort, und ich habe das OP um Klärung gebeten. Würde dich wieder wählen, wenn ich könnte.
Rick Hitchcock
1

Haskell , 251 Bytes

(#([],[]))
(x:r)#(o,n)|x>'9'=r#h(o++[x],n)|[(a,t)]<-lex$x:r=t#h(o,read a:n)
_#(o,n:_)=n
h=e.e
e(o:s,n:m:r)|o>'N'=e(s,g[o]n m:r)
e(o:s,n:r)|o=='D'=e(s,n:n:r)|o=='N'=e(s,-n:r)
e(o:s,n)|(p,m)<-e(s,n)=(o:p,m)
e t=t
g"a"=(+)
g"m"=(*)
g"q"=(((0^).abs).).(-)

Probieren Sie es online! Verwendet die folgenden Zeichen: azur Addition, mzur Multiplikation, qzur Gleichheit, Dzum Duplizieren und Nzur Negation. (Ich wollte eGleichheit verwenden, stieß aber auf das Problem, lexdas 2e3als Zahl analysiert wird .) Beispiel: (#([],[])) "2a3 4m"Erträge 20.

Laikoni
quelle
1

6502 Maschinencode (C64), 697 Bytes

00 C0 A2 00 86 29 86 26 86 27 86 28 86 4B 86 4C 86 CC 20 E4 FF F0 FB C9 0D F0
10 C9 20 30 F3 A6 27 9D B7 C2 20 D2 FF E6 27 D0 E7 C6 CC A9 20 20 1C EA A9 0D
20 D2 FF 20 95 C0 20 09 C1 20 95 C0 A6 26 E4 27 F0 4E BD B7 C2 E6 26 C9 20 F0
E8 C9 2D D0 09 A6 4C A9 01 9D B7 C3 D0 32 C9 22 D0 09 A6 4C A9 02 9D B7 C3 D0
25 C9 2B D0 09 A6 4C A9 81 9D B7 C3 D0 18 C9 2A D0 09 A6 4C A9 82 9D B7 C3 D0
0B C9 3D D0 0D A6 4C A9 83 9D B7 C3 E6 28 E6 4C D0 A3 4C 8A C2 A6 29 F0 6F A4
28 F0 6B CA F0 4B C6 28 A6 4B E6 4B BD B7 C3 F0 F5 30 14 AA CA D0 0B 20 7B C2
20 E1 C1 20 4D C2 D0 D9 20 5C C2 D0 D4 29 0F AA CA D0 0B 20 6D C2 20 08 C2 20
4D C2 D0 C3 CA D0 0B 20 6D C2 20 16 C2 20 4D C2 D0 B5 20 6D C2 20 F4 C1 20 4D
C2 D0 AA A4 4B B9 B7 C3 F0 02 10 AC C8 C4 4C F0 0F B9 B7 C3 F0 F6 30 F4 AA A9
00 99 B7 C3 F0 A6 60 A0 00 A6 26 E4 27 F0 15 BD B7 C2 C9 30 30 0E C9 3A 10 0A
E6 26 99 37 C4 C8 C0 05 D0 E5 C0 00 F0 08 A9 00 99 37 C4 20 39 C2 60 A2 FF E8
BD 37 C4 D0 FA A0 06 88 CA 30 0A BD 37 C4 29 0F 99 37 C4 10 F2 A9 00 99 37 C4
88 10 F8 A9 00 8D 3D C4 8D 3E C4 A2 10 A0 7B 18 B9 BD C3 90 02 09 10 4A 99 BD
C3 C8 10 F2 6E 3E C4 6E 3D C4 CA D0 01 60 A0 04 B9 38 C4 C9 08 30 05 E9 03 99
38 C4 88 10 F1 30 D2 A2 06 A9 00 9D 36 C4 CA D0 FA A2 10 A0 04 B9 38 C4 C9 05
30 05 69 02 99 38 C4 88 10 F1 A0 04 0E 3D C4 2E 3E C4 B9 38 C4 2A C9 10 29 0F
99 38 C4 88 10 F2 CA D0 D6 C0 05 F0 06 C8 B9 37 C4 F0 F6 09 30 9D 37 C4 E8 C8
C0 06 F0 05 B9 37 C4 90 F0 A9 00 9D 37 C4 60 A9 FF 45 FC 85 9F A9 FF 45 FB 85
9E E6 9E D0 02 E6 9F 60 A2 00 86 9F A5 FB C5 FD D0 07 A5 FC C5 FE D0 01 E8 86
9E 60 A5 FB 18 65 FD 85 9E A5 FC 65 FE 85 9F 60 A9 00 85 9E 85 9F A2 10 46 FC
66 FB 90 0D A5 FD 18 65 9E 85 9E A5 FE 65 9F 85 9F 06 FD 26 FE CA 10 E6 60 20
33 C1 A6 29 AD 3D C4 9D 3F C4 AD 3E C4 9D 3F C5 E6 29 60 A6 29 A5 9E 9D 3F C4
A5 9F 9D 3F C5 E6 29 60 A6 29 BD 3E C4 9D 3F C4 BD 3E C5 9D 3F C5 E6 29 60 C6
29 A6 29 BD 3F C4 85 FD BD 3F C5 85 FE C6 29 A6 29 BD 3F C4 85 FB BD 3F C5 85
FC 60 A6 29 BD 3E C5 10 13 20 7B C2 20 E1 C1 20 4D C2 A9 2D 20 D2 FF A6 29 BD
3E C5 8D 3E C4 BD 3E C4 8D 3D C4 20 8B C1 A9 37 A0 C4 4C 1E AB

Online-Demo

Verwendung sys49152 , geben Sie dann den Ausdruck anyfix ein und drücken Sie die Eingabetaste.

  • Fast keine Fehlerprüfung, also erwarten Sie lustige Ausgaben für ungültige Ausdrücke.
  • Das Symbol für die Multiplikation ist *, alle anderen wie vorgeschlagen.
  • Die maximale Eingabelänge beträgt 256 Zeichen. Es können maximal 127 Operatoren in die Warteschlange gestellt werden.
  • Die Eingaberoutine verarbeitet keine Steuerzeichen. Geben Sie also keine Fehler ein.
  • Ganzzahlen sind 16-Bit-signiert, ein Überlauf wird stillschweigend durchgeführt.
  • Die Byteanzahl ist etwas groß, da diese CPU nicht einmal die Multiplikation kennt und das C64-Betriebssystem / ROM keine Ganzzahlarithmetik oder Konvertierungen in / von Dezimalzeichenfolgen liefert .

Hier ist der lesbare Assembler-Quellcode im Ca65-Stil .

Felix Palmen
quelle
1

VB.NET (.NET 4.5) 615 576 Bytes

-39 Bytes dank Felix Palmen durch den Wechsel \r\nzu\n

Benötigt Imports System.Collections.Generic(in Byteanzahl enthalten)

Dim s=New Stack(Of Long),q=New List(Of String),i=Nothing,t=0,c=0
Function A(n)
For Each c In n
If Long.TryParse(c,t)Then
i=i &"0"+t
Else
If c<>" "Then q.Add(c)
If i<>A Then s.Push(i)
i=A
End If
If i=A Then E
Next
If i<>A Then s.Push(i)
E
A=s
End Function
Sub E()
c=s.Count
If c=0Then Exit Sub
For Each op In q
If op="-"Or op="d"Or c>1Then
Select Case op
Case"*"
s.Push(s.Pop*s.Pop)
Case"+"
s.Push(s.Pop+s.Pop)
Case"="
s.Push(-(s.Pop=s.Pop))
Case"-"
s.Push(-s.Pop)
Case"d"
s.Push(s.Peek)
End Select
q.Remove(op)
E
Exit Sub
End If
Next
End Sub

Der Einstiegspunkt ist Function A, die eine Zeichenfolge als Eingabe annimmt und a zurückgibt Stack(Of Long).

Symbole:

  • Zusatz - +
  • Multiplikation - *
  • Gleichberechtigung - =
  • Verneinung - -
  • Vervielfältigung - d

Überblick:

Funktion Animmt die Eingabe und tokenisiert sie. Es verwendet a Long?, um eine laufende Summe für mehrstellige Ganzzahlen zu erstellen, dieNothing erstellen. Dies bedeutet, dass derzeit keine Ganzzahl gelesen wird.

Subroutine Enimmt den Stapel von ganzen Zahlen und die Warteschlange von Operatoren und wertet die Anyfix-Notation aus. Es ruft sich rekursiv auf, bis keine Aktionen mehr vorhanden sind.

Ich deklariere globale Parameter auf einmal, um Bytes sowohl bei der Deklaration als auch bei der Parameterübergabe zu sparen.

Der Rückgabewert von Awird festgelegt, indem der übereinstimmenden Variablen ein Wert zugewiesen wird A.

VB Trueist äquivalent zu -1, so dass die Operation das Ergebnis negieren muss, um den korrekten Wert zu erhalten.

Probieren Sie es online!

Brian J
quelle
Zum Hinzufügen vorschlagen Online testen!
Felix Palmen
Übrigens, mit Imports, bekomme ich nur 576eine Byteanzahl von - hättest du falsch gezählt?
Felix Palmen
@FelixPalmen Ich habe \r\nstattdessen mit gezählt \n, also ist das der Punkt , an dem die Diskrepanz besteht.
Brian J
@FelixPalmen TIO hinzugefügt, danke, dass du mich daran erinnert hast! :) (Oh, ich wusste nicht, dass du es schon für diesen Beitrag geschafft hast)
Brian J