Operationen der Ordnung

13

Einführung

In der Kindheit kann es vorkommen, dass Sie das Addieren und Multiplizieren beherrschen. Dann kommt jemand und informiert Sie darüber, dass:

a * b + c = (a * b) + c! = a * (b + c),

und dass es kein so einfacher oder linearer Prozess war, wie Ihnen zuvor beigebracht wurde. Sie erfahren, dass es etwas gibt, das als Operationsreihenfolge bezeichnet wird . Dies ist eine sehr wichtige Methode, um ein gewisses Maß an Konsistenz und Ausdrucksweise beizubehalten, ohne dass Klammern im Weg stehen.

Generische Handlung

Eines Tages wachst du mit Panik auf den Straßen auf. Eine extremistische Gruppe unter dem Namen " The 2560 " (Abkürzung für "Organization Against the Order of Operations") hat mit ihren bösen Methoden die Kontrolle über alle Atomwaffen der Welt übernommen. Sie halten den ganzen Planeten als Geiseln und haben eine einfache Forderung: kehren Sie die akzeptierte Reihenfolge der Operationen oder der Ausrottung des Gesichts um (Klammern sollen ihre Priorität behalten). Das neue System heißt PSADME (Klammern, Subtraktion / Addition, Division / Multiplikation, Exponenten), und Ausdrücke werden von rechts nach links ausgewertet:

a - b - c = a - (b - c) = a + c - b

Tage vergehen und der Übergang ist im Gange. Während Mathematiker und Physiker damit beschäftigt sind, ihre Gleichungen neu zu schreiben, stehen die Informatiker vor der Aufgabe, die Art und Weise zu ändern, in der mathematische Ausdrücke von Computern interpretiert werden. Sie gehören zu einer geheimen Rebellen-Programmgruppe, die den neuen globalen Overlords so viel Leid zufügen soll - und Sie werden von The 2560 zufällig ausgewählt und mit der Erstellung des Benchmark-Berechnungsprogramms beauftragt.

Deine Mission

Schreiben Sie ein Programm (oder eine Funktion), das einen (numerischen) mathematischen Ausdruck als Eingabe verwendet, den Ausdruck unter Verwendung von PSADME als Reihenfolge der Operationen berechnet und das Ergebnis ausgibt. Ausdrücke sollten von rechts nach links ausgewertet werden, also

1-3+4=1-7=-6.

Der Einfachheit halber sind alle angegebenen Zahlen Ganzzahlen, und die Berechnungen führen zu ganzzahligen Ergebnissen.

Regeln und Wertung

  • Das Programm sollte Eingaben mit einer Länge von bis zu 128 Zeichen akzeptieren - wenn Ihre Sprache / Plattform eine niedrigere maximale Eingabelänge hat, ist dies eine akzeptable Ausrede.
  • Standardlücken sind verboten.
  • Der Gewinncode wird am 18. November (4 Wochen ab diesem Postdatum) ausgewählt.
  • Sie können gerne eine Postleitzahl eingeben, die nicht als golfwürdig eingestuft wird. Hier geht es um Spaß. Wenn Sie eine interessante Möglichkeit haben, dies aber nicht selbst tun können (oder aufgrund Ihrer Methode), können Sie es trotzdem posten.

Wie üblich ist der Gewinncode der mit der geringsten Anzahl von Bytes, mit einigen Unterhaltungswertboni:

  • -5, um die Verwendung der Zeichen im angegebenen Ausdruck zu vermeiden: + , - , ( , ) , ^ , * , /
  • -5 Die Berechnung auf einem Standardcomputer dauert länger als 5 Minuten (jedoch nicht länger als 10 Minuten), ohne dass die Methode offensichtlich ist (unter Verwendung der Uhr oder unnötiger Schleifen). Ziel ist es, die neuen Overlords davon zu überzeugen, dass Sie nicht versuchen , ihre Schicksalsberechnungen zu stören.
  • - (5 + N) für eine direkte beleidigende Nachricht (Länge N, ohne führende / nachfolgende Leerzeichen) über die Mitglieder von The 2560, die in Ihrem Code gut sichtbar geschrieben werden soll, mit einer lächerlichen Erklärung, warum dies erforderlich ist Dort. Wenn es entfernt wird, muss der Code nicht richtig funktionieren. Ja, kostenlose Punkte für den Unterhaltungswert.

Beispiele und Erklärungen

[program] 2 - 2 - 2
2

2 - (2 - 2) = 2

[program] (2 + 2 * 3 + 3) / 3 + 3
4

(4 · 6) / (3 + 3) = 4

[program] 3 + 2 + 1 ^ 3
216

(3 + 2 + 1) ^ 3 = 216

[program] -5^2
25

(-5) ^ 2 = 25

[program] 32 / 8 * 3 - 1
2

32 / (8 * (3 - 1)) = 32/16 = 2

Jake
quelle
1 - 3 + 4 = 1 - 7? Von rechts nach links würde dies nahelegen, aber das setzt im Gegensatz zu PSADME Addition vor Subtraktion, nein?
LLlAMnYP
1
@LLlAMnYP Addition und Subtraktion befinden sich in derselben "Gruppe" wie in PEMDAS, also von rechts nach links. Gleiches gilt für Multiplizieren / Dividieren. Es ist eher so P(SA)(DM)E.
Geobits
2
Die Anweisung soll nicht von rechts nach links verarbeitet werden, sondern Vorgänge mit gleicher Priorität werden von rechts nach vorne bewertet. Also 4/2 = 2, 2-1 = 1, aber a / b c = a / (b c) und nicht das übliche (a / b) * c. Ich hoffe das klärt die Dinge auf.
Jake
Der wahrscheinlich einfachste Weg, dies zu tun, besteht darin, eine Flex / Bison- oder Lex / Yacc-Grammatik zu schreiben.
5
Sie sollten das Akronym in PADME ändern , da Mitglieder einer so bösen Organisation die neuere Star Wars-Trilogie mit Sicherheit mehr mögen als die Originale. Es ist auch leichter zu merken.
mbomb007

Antworten:

9

Haskell, 134 Bytes

import qualified Prelude as P
infixl 6 ^
(^)=(P.^)
infixr 8 + 
(+)=(P.+)
infixr 8 - 
(-)=(P.-)
infixr 7 /
(/)=P.div
infixr 7 *
(*)=(P.*)

Neudefinition der mathematischen Operatoren mit neuen Fixities und Prioritäten. Jetzt:

*Main> 32 / 8 * 3 - 1
2
nimi
quelle
1
Beeindruckend. Einfach wow. Ist das überhaupt in einer anderen Sprache möglich? +1
ETHproductions
Ich war mir ziemlich sicher, dass es in Mathematica möglich war oder zumindest ein ähnlicher Ansatz, aber schnell wurde mir klar, dass mir das Wissen dazu fehlt.
LLlAMnYP
1
Ich bin neu genug, um mir nicht sicher zu sein, ob der folgende Vorschlag in diesem Forum normalerweise akzeptabel ist. Es basiert vollständig auf Ihrem Code, ist jedoch ein Bash-Skript, das Perl verwendet, um die Haskell-Datei zu generieren und an GHCi zu übergeben. Auf diese Weise spare ich ein GANZES BYTE. perl -e'$_="import qualified Prelude as Pl 6^r 8+r 8-r 7*r 7/";s/(. \d(.))/\ninfix\1\n(\2)=(P.\2)/g;s~\./~.div~;print'>a.hs;ghci a.hs Leider hat ein Tippfehler dazu geführt, dass der generierte Code kein Leerzeichen zwischen der Ziffer und dem Symbol enthält, aber trotzdem einwandfrei funktioniert. Dies bedeutet, dass Ihr Code 5 Bytes verlieren kann und meine "Verbesserung" übertrifft.
Jake
@JArkinstall Was auch immer es wert ist, meine Antwort wird effektiv verwendet sed, um Shell-Code zu generieren und auszuwerten. Wahrscheinlich eine gute Meta-Frage.
Digital Trauma
Das ist wahr, und ich mag Ihren Ansatz sehr - es scheint jedoch einen Schritt weiter zu gehen, ein Tool (perl oder sed) zu verwenden, um eine Datei in einer Sprache zu generieren, die dann in eine andere Sprache eingelesen wird. Ich wäre nicht allzu überrascht, wenn es eine Möglichkeit gäbe, den obigen Code über einen anderen Generator zu erzeugen (obwohl mir die Methode nicht klar ist!), Und wir würden uns in einer parse-ception befinden. Wenn dies zulässig ist, könnte man diesen Ansatz sogar auf Ihren Code anwenden (und einige Beispiele, die ich in einigen der besser lesbaren Antworten auf einige Herausforderungen auf diesem Board gesehen habe).
Jake
2

GNU sed -r mit exec-Erweiterung, 398

s@ *\^ *@ ** @
:
s@\((-?[0-9]+)\)@\1@
t
s@(-?[0-9]+ - )+(-?[0-9]+ - -?[0-9]+)@\1(\2)@
t
s@(.*(^| |\())(-?[0-9]+ [-+] -?[0-9]+)(.*)@echo '\1'$((\3))'\4'@e
t
s@(-?[0-9]+ / )+(-?[0-9]+ / -?[0-9]+)@\1(\2)@
t
s@(.*(^| |\())(-?[0-9]+ [*/] -?[0-9]+)(.*)@echo '\1'$((\3))'\4'@e
t
s@(-?[0-9]+ \*\* )+(-?[0-9]+ \*\* -?[0-9]+)@\1(\2)@
t
s@(.*(^| |\())(-?[0-9]+ \*\* -?[0-9]+)(.*)@bash -c 'echo \1$[\3]\4'@e
t

Nicht besonders kurz, aber erledigt den Job.

sed ist in Ordnung, um die Priorität zu analysieren, führt aber keine Arithmetik durch. Daher verwenden wir die GNU sed exec-Erweiterung des sBefehls, um die erforderliche Arithmetik an die Shell auszulagern.

Vorerst gehen alle Bediener davon aus, mit Ausnahme von ^genau einem Leerzeichen davor und dahinter.

Testausgang:

$ cat psadme.txt 
2 - 2 - 2
(2 + 2 * 3 + 3) / 3 + 3
3 + 2 + 1 ^ 3
-5^2
32 / 8 * 3 - 1
$ sed -rf psadme.sed psadme.txt 
2
4
216
25
2
$ 
Digitales Trauma
quelle
Schönes Profilfoto. xD
Oliver Ni
1

JavaScript (ES6) 287 300

Edit Bug behoben (nur ein Tippfehler, 6 hätte 4 sein müssen) - Eine vollständige Erklärung wurde am Ende des Snippets hinzugefügt

Bearbeiten 2 einer anderen Herausforderung wurde eine Verbesserung festgestellt

Noch eine Portierung des gleichen Parsers mit nur minimalen Unterschieden. (vergleiche damit )

f=(x,W=[],Q=['('],z=1,h=p=>'+-*/^^))('.indexOf(p)>>1,C=n=>{for(;h(q=Q.pop())<h(n);W.push(q=='^'?Math.pow(a,b):eval(`a${q}b`)))a=W.pop(b=W.pop());z&&Q.push(q,n)})=>((x+')').replace(/\d+|\S/g,t=>t>'('?t>'('?~h(t)?z&&t=='-'?z=-z:C(t,z=1):(W.push(z*t),z=0):C(t,z=0):(Q.push(t),z=1)),W.pop())

// More readable
U=(x,W=[],Q=['('],z=1,
  h=p=>'+-*/^^))('.indexOf(p)>>1,
  C=n=>{
    for(;h(q=Q.pop())<h(n);
        W.push(q=='^'?Math.pow(a,b):eval(`a${q}b`)))
      a=W.pop(b=W.pop());
    z&&Q.push(q,n)
  }
)=>(
  (x+')')
  .replace(/\d+|\S/g,t=> 
       t>'('
       ?t>'('
       ?~h(t)
       ?z&&t=='-'?z=-z:C(t,z=1)
       :(W.push(z*t),z=0)
       :C(t,z=0)
       :(Q.push(t),z=1)
  ),
  W.pop()
)

// TEST
console.log=(...x)=>O.innerHTML+=x.join` `+'\n'

console.log(f('1 - 3 + 4')) // -6
console.log(f('2-2-2')) // 2
console.log(f('(2 + 2 * 3 + 3) / 3 + 3')) // 4
console.log(f('3 + 2 + 1 ^ 3')) // 216
console.log(f('-5^2')) // 25
console.log(f('32 / 8 * 3 - 1')) // 2

// Explained
X=(x,W=[],Q=['('],z=1,
  h=p=> // operator priority '+-:1, */:3, ^:5, ):7, (:9. If an operand then -1
     '+-*/^^))('.indexOf(p)>>1,
  C=n=>{ // Evaluate operators present on stack if they have upper priority, then push new operator on stack
    //console.log('Operand '+n)
    while( h(q = Q.pop()) < h(n) ) // pop operator from op stack and compare priority with current
    {
      // Pop operands from stack and push result
      b = W.pop();
      a = W.pop();
      r = q=='^' ? Math.pow(a,b) : eval('a'+q+'b')
      // console.log('Evaluate '+a+q+b+'='+r)
      W.push(r);
    }
    // if z == 0 do nothing, because the current operands are '(' and ')' that must be discarded
    // else Push again the last operator popped and the current one
    z && Q.push(q, n) // 
  }
)=>(
  (x+')')
  .replace(/[\d.]+|\S/g,t=> {
    //console.log('Q:'+Q,'W:'+W,'T:'+t,'U:'+h(t),'Z:'+z), // display status
    if (t == '(') 
    { // open parenthesis
      z = 1
      Q.push(t) // push a operator, its the highest priority
    }
    else if (t == ')')
    { //close parenthesis
      z = 0
      C(t) 
    }
    else if (h(t) < 0)
    { // operand
      W.push(z*t) // push operand with right sign
      z = 0 // set z to 0 to mark that we just pushed an operand, so next '-' (if present) is a binary operator 
    }
    else
    { // operator
      if (z && t=='-') // the minus is an unary operator (the only unary operator allowed is '-', '+' will not work)
        z =-z // change the sign
      else
        z = 1, // no unary minus
        C(t)
    }    
  }),
  W.pop() // return the value at top of operand stack
)
<pre id=O></pre>

edc65
quelle