Ottimizza il compilatore per un semplice linguaggio di programmazione della notazione polacca inversa


24

Descrizione

Il linguaggio di programmazione immaginario (IPL) utilizza la notazione inversa polacca. Ha i seguenti comandi:

  • i - inserisci il numero e spingilo nello stack
  • o - uscita non distruttiva in cima allo stack (il numero rimane nello stack)
  • d - scarta la parte superiore dello stack
  • numero intero : spingere questo numero nello stack
  • + - * - estrae due numeri dallo stack, esegue l'operazione corrispondente e respinge il risultato. Non esiste alcuna divisione in IPL.

IPL funziona solo con numeri interi e viene utilizzato per calcoli semplici. Un programma IPL è scritto su una riga e separato da spazi. La stringa vuota è un programma IPL valido.

Programma IPL:

i i + o 

Inserisce due numeri, li somma e genera il risultato.

I numeri di input e i numeri interi che possono essere inseriti nello stack sono compresi nell'intervallo [-999, 999], tuttavia l'output può essere qualsiasi. Se la tua lingua non supporta numeri grandi, va bene comunque.

Formato di input / output

Puoi scegliere qualsiasi formato di input / output purché sia ​​chiaro per capire e leggere / scrivere: stringa, elenco, token ecc.

Compito

Ti viene dato un programma IPL, devi ottimizzarlo (riduci la lunghezza):

i 12 + 3 + o d 2 3 + d

Dopo l'ottimizzazione diventerà

i 15 + o

Non è necessario preservare lo stato dello stack, ma la quantità di input e output e il loro ordine devono corrispondere per il programma originale e ottimizzato.

Quindi programma IPL:

-40 i * 2 * o i + 3 1 + o i 2 *

Dopo l'ottimizzazione diventerà

i -80 * o i 4 o i

o

-80 i * o i 4 o i

(nota che devi salvare tutti gli input, anche se sono irrilevanti).

Non ci dovrebbe essere hardcoding per casi di test, il codice dovrebbe funzionare su qualsiasi programma IPL arbitrario e produrre il programma IPL più breve possibile che soddisfi i requisiti.

punteggio

Punteggio di golf di codice predefinito.

AGGIORNAMENTO: modificato il punteggio in punteggio di puro codice golf, come suggerito da @Sanchises.

Casi test:

Ingresso:

(empty string)

Uscita possibile:

(empty string)

Ingresso:

i 4 * 2 + 3 * 6 - o

Uscita possibile:

i 12 * o

Ingresso:

1 1 + o

Uscita possibile:

2 o

Ingresso:

i 2 + 3 + o d 2 3 + d

Uscita possibile:

i 5 + o

Ingresso:

-40 i * 2 * o i + 3 1 + o i 2 *

Uscita possibile:

-80 i * o i 4 o i

Ingresso:

i i 1 + i 1 + i 1 + i 1 + d d d d o 

Uscita possibile:

i i i i i d d d d o 

Ingresso:

i i i 0 * * * o

Uscita possibile:

i i i 0 o

Ingresso:

i i i 1 * * * o

Uscita possibile:

i i i * * o

Ingresso:

i 222 + i 222 - + o

Uscita possibile:

i i + o

Ingresso:

i 2 + 3 * 2 + 3 * 2 + 3 * i * d i 2 + 3 * i + d i o 2 + 2 - 0 * 1 o

Uscita possibile:

i i i i i o 1 o

Ingresso:

i 1 + 2 * 1 + o 

Uscita possibile:

i 2 * 3 + o

Ingresso:

1 1 + o i 2 + 3 + o d 2 3 + d 4 i * 2 * o i + 3 1 + o i 2 * i i 1 + i 1 + i 1 + i 1 + d d d d o i i i 0 * * * o i i i 1 * * * o i 2 + i 2 - + o i 2 + 3 * 2 + 3 * 2 + 3 * i * d i 2 + 3 * i + d i o 2 + 2 - 0 * 1 o

Uscita possibile:

2 o i 5 + o 8 i * o i 4 o i i i i i i d d d d o i i i 0 o i i i * * * o i i + o i i i i i o 1 o

1
Una domanda: si può semplificare i i d oal i o i(l'ingresso è in ordine e l'uscita è in ordine) o non si dovrebbe semplificare? (l'insieme di input e output dovrebbe essere in ordine)
Sanchises

1
@Sanchise no, input e output dovrebbero essere in ordine. Se il programma originale immette 2 numeri prima di emettere qualcosa di ottimizzato, dovrebbe fare lo stesso.
Андрей Ломакин,

1
Benvenuti in PPCG! Bella prima sfida!
Luis felipe De jesus Munoz,

6
Dalla coda delle recensioni , non credo che questa sfida non sia chiara. Se lo fai, ti preghiamo di commentare il perché.
mbomb007,

2
@WW Penso che l'OP significhi che non dovresti codificare solo i casi di test elencati nella domanda. Devi supportare input arbitrari. Non ci dovrebbero essere hardcoding per i casi di test, il codice dovrebbe funzionare su qualsiasi programma IPL arbitrario
mbomb007

Risposte:


5

Wolfram Language (Mathematica) , 733 728 690 564 516 506 513 548 byte

j=Integer;f=Flatten;s=SequenceReplace;A=FixedPoint[f@s[#,{{x_j,p,y_j,t}->{y,t,x*y,p},{x_j,y_j,p}->x+y,{x_j,y_j,t}->x*y,{x_j,p,y_j,p}->{x+y,p},{x_j,t,y_j,t}->{x*y,t},{0,p}|{1,t}->{},{0,t}->{d,0}}]//.{a___,Except[i|o]}->{a}&,#]&;B=Expand@Check[f@FoldPairList[f/@Switch[#2,i,{{i},{#,i@c++}},o,{{Last@#},#},d,{{},Most@#},p,{{},{#[[;;-3]],Tr@#[[-2;;]]}},t,{{},{#[[;;-3]],#[[-2]]*Last@#}},_,{{},{##}}]&,c=0;{},#],x]&;F=MinimalBy[w=A@f[#/.m->{-1,t,p}];z=B@w;s[#,{-1,t,p}->m]&/@A/@Select[Permutations@Join[w,Cases[z /.i@_->i,_j,∞]],B@#==z&],Length][[1]]&

Provalo online!

Questo è un tour-de-force in quattro passaggi che (1) sostituisce "-" con "-1 * +" in modo da non dover gestire le sottrazioni, (2) semplifica un po 'l'elenco dei comandi ( 3) crea un elenco di tutte le permutazioni di questo elenco di comandi e seleziona quelli che danno lo stesso risultato quando analizzato (eseguito), e (4) semplifica un po 'questi elenchi di comandi e seleziona il più breve, dopo aver riconvertito alcune operazioni in sottrazioni.

Questo codice è terribilmente inefficiente perché passa attraverso l'elenco di tutte le permutazioni del codice di input. Per codici di input lunghi non consiglio di eseguire questo codice; ma mentre lo leggo non ci sono restrizioni di runtime o memoria in questa sfida.

Questo codice esegue la fase di ottimizzazione dopo aver convertito tutte le operazioni "-" in operazioni "+" con i segni capovolti e solo alla fine reintroduce l'operatore "-" durante la riconversione del codice in stringhe. Ciò implica ad esempio che "i -1 i * + o" è correttamente ottimizzato su "ii - o".

Poiché il requisito del formato I / o è piuttosto lento, questo codice accetta e restituisce il codice come elenchi, in cui i simboli "+", "-", "*" sono rappresentati rispettivamente da p, m, t, token. La conversione da e in stringhe viene eseguita nella funzione wrapper fornita su TIO:

G[S_] := StringReplace[{"p" -> "+", "m" -> "-", "t" -> "*"}]@StringRiffle@
         Quiet@F@
         ToExpression[StringSplit[S] /. {"+" -> p, "-" -> m, "*" -> t}]

Versione non giocata a golf, incluso il wrapper in formato stringa e la riduzione al minimo della lunghezza della stringa di codice finale anziché il numero di token, e incluse alcune altre modifiche di trasformazione:

(* convert code string to list of operators *)
inputfilter[s_] := ToExpression[Flatten[StringSplit[s] /.
  {"i" -> i, "o" -> o, "d" -> d, "+" -> p, "-" -> {-1, t, p}, "*" -> t}]]

(* convert list of operators to code string *)
outputfilter[s_] := StringReplace[StringRiffle@Flatten@SequenceReplace[s,
  {{-1, t, p} -> m,                         (* convert "-1 t p" back to "-"             *)
   {x_ /; x < 0, p} -> {-x, m},             (* convert "y x +" to "y -x -" when x<0     *)
   {x_ /; x < 0, t, p} -> {-x, t, m}}],     (* convert "y x * +" to "y -x * -" when x<0 *)
  {"m" -> "-", "p" -> "+", "t" -> "*"}]     (* backsubstitution of symbols              *)

(* simplify a list of operators somewhat *)
simplifier[s_] := FixedPoint[Flatten@SequenceReplace[#,
  {{x_Integer, p, y_Integer, t} -> {y, t, x*y, p},  (*  "x + y *" -> "y * (xy) +"       *)
   {x_Integer, y_Integer, p} -> x + y,              (*  "x y +" -> "(x+y)"              *)
   {x_Integer, y_Integer, t} -> x*y,                (*  "x y *" -> "(xy)"               *)
   {x_Integer, p, y_Integer, p} -> {x + y, p},      (*  "x + y +" -> "(x+y) +"          *)
   {x_Integer, t, y_Integer, t} -> {x*y, t},        (*  "x * y *" -> "(xy) *            *)
   {0, p} | {1, t} -> {},                           (*  "0 +" and "1 *" are deleted     *)
   {x_Integer, i, p} -> {i, x, p},                  (*  "x i +" -> "i x +"              *)
   {x_Integer, i, t} -> {i, x, t},                  (*  "x i *" -> "i x *"              *)
   {0, t} -> {d, 0}}] //.                           (*  "0 *" -> "d 0"                  *)
  {a___, Except[i | o]} -> {a} &, s]                (* delete trailing useless code     *)

(* execute a list of operators and return the list of generated outputs *)
parse[s_] := Expand@Quiet@Check[Flatten@FoldPairList[  (* stack faults are caught here     *)
  Function[{stack, command},                        (* function called for every command*)
    Flatten /@ Switch[command,                      (* code interpretation:             *)
    i, {{i}, {stack, i[inputcounter++]}},           (* output "i" and add input to stack*)
    o, {{stack[[-1]]}, stack},                      (* output top of stack              *)
    d, {{}, Most[stack]},                           (* delete top of stack              *)
    p, {{}, {stack[[;; -3]], stack[[-2]] + stack[[-1]]}},  (* add two stack elements    *)
    t, {{}, {stack[[;; -3]], stack[[-2]]*stack[[-1]]}},    (* multiply two stack elements*)
    _, {{}, {stack, command}}]],                    (* put number onto stack            *)
    inputcounter = 0; {},                           (* start with zero input counter and empty stack*)
    s],                                             (* loop over code list              *)
  x]                                                (* return "x" if an error occurred  *)

(* the main function that takes a code string and returns an optimized code string *)
F[s_] := Module[{w, q},
  w = simplifier@inputfilter@s;      (* convert input to useful form *)
  q = parse[w];                      (* execute input code *)
  MinimalBy[
    outputfilter@*simplifier /@      (* simplify and stringify selected codes          *)
      Select[Permutations[w],        (* all permutations of code list                  *)
             parse[#] == q &],       (* select only those that give the correct output *)
    StringLength] // Union]          (* pick shortest solution by length               *)

Grazie a @redundancy per la cattura di un bug: il parser ha bisogno di essere Expandapplicato all'output per gestire l'equivalenza distributiva. 506 → 513

aggiornare

Ora ottimizza anche 1 o 1 + oa 1 o 2 o. Questo è stato un caso sorprendentemente difficile e ha reso il codice molto più lento. 513 → 548


Sembra che questo dia un errore sul test case i i 1 + i 1 + i 1 + i 1 + d d d d o.
Grimmy,

@Grimy come ho detto, questo codice non funziona per grossi problemi perché passa attraverso un'esaustiva ricerca combinatoria dello spazio del codice. Il tuo errore è un errore di memoria insufficiente su TIO e non è dovuto al mio codice.
Roman

@Grimy per "ii 1 + d o" il mio codice dà "iid o", che considero ottimizzato. Per "ii 1 + i 1 + dd o" indica "iii + d o", che ha lo stesso numero di token della più ovvia ottimizzazione "iiidd o". Non ho provato input più lunghi.
Roman

Credo che l'input i 2 * i 2 * + odovrebbe produrre output ottimizzato i i + 2 * o, ma questo codice restituisce l'input (non ottimizzato).
ridondanza il

Grazie @redundancy, è stato risolto e il tuo esempio è ora uno dei casi di test inclusi.
Roman
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.