Operazioni di ordine


13

introduzione

Arriva un punto nell'infanzia quando pensi di aver imparato ad aggiungere e moltiplicare, poi qualcuno arriva e ti informa che:

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

e che non era un processo semplice o lineare come ti era stato insegnato in precedenza. Si impara che esiste qualcosa chiamato l' ordine delle operazioni . Questo è un modo molto importante per mantenere un certo livello di coerenza ed espressione, senza che le parentesi si frappongano.

Trama generica

Un giorno ti svegli al suono del panico nelle strade. Un gruppo estremista sotto il nome di " The 2560 " (abbreviazione di "Organizzazione contro l'ordine delle operazioni", con una strana svolta esadecimale) hanno usato i loro metodi malvagi per prendere il controllo di tutte le armi nucleari del mondo. Stanno tenendo in ostaggio l'intero pianeta e hanno una semplice richiesta: invertire l'ordine accettato delle operazioni o affrontare l'eradicazione (le parentesi devono mantenere la loro priorità). Il nuovo sistema si chiama PSADME (parentesi, sottrazione / addizione, divisione / moltiplicazione, esponenti) e le espressioni valutano da destra a sinistra:

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

I giorni passano e la transizione è in corso. Mentre matematici e fisici sono tutti impegnati a riscrivere le loro equazioni, gli informatici devono affrontare il compito di cambiare il modo in cui le espressioni matematiche vengono interpretate dai computer. Fai parte di un gruppo segreto di programmazione ribelle che mira a provocare tanto tormento per i nuovi signori globali - e, per caso, sei selezionato casualmente da The 2560 e incaricato di produrre il programma di calcolo di riferimento.

La tua missione

Scrivi un programma (o una funzione) che prende come input un'espressione matematica (numerica), calcola l'espressione usando PSADME come ordine delle operazioni e genera il risultato. Le espressioni dovrebbero essere valutate da destra a sinistra, quindi

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

Per semplicità, tutti i numeri forniti saranno numeri interi e i calcoli produrranno risultati interi.

Regole e punteggio

  • Il programma dovrebbe accettare input con una lunghezza massima di 128 caratteri - se la tua lingua / piattaforma ha una lunghezza di input massima inferiore, questa è una scusa accettabile.
  • Sono vietate le scappatoie standard.
  • Il codice vincente sarà scelto il 18 novembre (4 settimane da questa data di pubblicazione).
  • Sentiti libero di inserire un codice che non sarebbe considerato degno di golf. Si tratta di divertimento. Se hai un modo interessante di farlo ma non riesci a giocare da solo (o per natura del tuo metodo), puoi pubblicarlo comunque.

Come al solito, il codice vincente è quello con il minor numero di byte, con alcuni bonus di valore di intrattenimento:

  • -5 per evitare qualsiasi uso dei caratteri nell'espressione fornita: + , - , ( , ) , ^ , * , /
  • -5 per eseguire i calcoli richiedono più di 5 minuti (ma non più di 10 minuti) per il calcolo su un computer standard, senza che il metodo sia ovvio (utilizzando l'orologio o i loop non necessari); Lo scopo è convincere i nuovi signori che non stai cercando di interrompere i loro calcoli di sventura.
  • - (5 + N) per un messaggio offensivo diretto (di lunghezza N, escluso lo spazio bianco iniziale / finale) sui membri di The 2560 da scrivere in bella vista all'interno del codice, con alcune ridicole spiegazioni sul perché debba essere Là. Se viene rimosso, il codice non deve funzionare correttamente. Sì, punti gratuiti per valore di intrattenimento.

Esempi e spiegazioni

[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


1 - 3 + 4 = 1 - 7? Da destra a sinistra lo suggerirei, ma questo sta mettendo l'aggiunta davanti alla sottrazione, contrariamente a PSADME, no?
LLlAMnYP,

1
@LLlAMnYP L'aggiunta e la sottrazione sono nello stesso "gruppo", proprio come in PEMDAS, quindi avvengono da destra a sinistra. Lo stesso vale per moltiplicare / dividere. È più simile P(SA)(DM)E.
Geobits il

2
L'istruzione non è pensata per essere elaborata da destra a sinistra, ma piuttosto le operazioni di uguale precedenza vengono valutate prima di tutto. Quindi 4/2 = 2, 2-1 = 1, ma a / b c = a / (b c) anziché il solito (a / b) * c. Spero che questo chiarisca le cose.
Jake,

Probabilmente il modo più semplice per farlo è scrivere una grammatica flex / bison o lex / yacc.

5
Dovresti cambiare l'acronimo in PADME , poiché i membri di un'organizzazione così malvagia apprezzerebbero sicuramente la nuova trilogia di Star Wars più degli originali. È anche più facile da ricordare.
mbomb007,

Risposte:


9

Haskell, 134 byte

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

Ridefinire gli operatori matematici con nuove fissità e priorità. Adesso:

*Main> 32 / 8 * 3 - 1
2

1
Wow. Solo wow. È possibile anche in un'altra lingua? +1
ETHproductions

Ne ero abbastanza sicuro, era possibile in Mathematica, o almeno un approccio simile, ma presto realizzato, mi mancava la conoscenza per farlo.
LLlAMnYP il

1
Sono abbastanza nuovo qui per essere incerto sul fatto che il seguente suggerimento sia generalmente accettabile su questo forum. Si basa interamente sul tuo codice, ma è uno script bash che utilizza Perl per generare il file Haskell e passarlo a GHCi. In questo modo, salvo un intero 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 Sfortunatamente, un errore di battitura ha reso il codice generato privo di uno spazio tra la cifra e il simbolo, ma funziona comunque bene. Questo significa che il tuo codice può perdere 5 byte e batte il mio "miglioramento".
Jake,

@JArkinstall Per quello che vale, la mia risposta sta effettivamente usando sedper generare e valutare il codice della shell. Probabilmente una buona meta domanda.
Trauma digitale,

Questo è vero, e mi piace molto il tuo approccio - tuttavia, usare uno strumento (perl o sed) per generare un file in una lingua che viene poi letto in un'altra lingua sembra un passo avanti. Non sarei troppo sorpreso se esistesse un modo per produrre il codice di cui sopra tramite un altro generatore (anche se il metodo non è ovvio per me!), E ci troveremmo nell'analisi. Se ciò è consentito, si potrebbe anche applicare questo approccio al codice (e alcuni esempi che ho visto in alcune delle risposte in lingua più leggibili ad alcune sfide in questo forum).
Jake,

2

GNU sed -r con estensione exec, 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

Non particolarmente breve, ma ottiene il lavoro fatto.

sed è OK per analizzare la precedenza ma non fa l'aritmetica. Quindi usiamo l'estensione GNU sed exec al scomando per esternalizzare l'aritmetica necessaria alla shell.

Per ora assume tutti gli operatori, ad eccezione di ^avere esattamente uno spazio davanti e dietro.

Uscita di prova:

$ 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
$ 

Bella immagine del profilo. xD
Oliver Ni

1

JavaScript (ES6) 287 300

Modifica bug risolto (solo un refuso, 6 avrebbe dovuto essere 4) - Aggiunta una spiegazione completa alla fine dello snippet

Modifica 2 Trovato alcuni miglioramenti lavorando su un'altra sfida

Ancora un altro porting dello stesso parser con solo una minima differenza. (confronta con questo )

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>

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.