¡Interpreta StackyMath!

14

¡Es hora de que implemente mi nuevo lenguaje basado en pila! Se llama StackyMath. Este será un lenguaje basado en la pila con 8 operaciones en la pila y formas de agregar números a la pila.

Lista de operaciones:

  • /: División. Realizado en los 2 primeros números de la pila. Empuja el resultado nuevamente en la pila.
  • *: Multiplicación. Realizado en los 2 primeros números de la pila. Empuja el resultado nuevamente en la pila
  • -: Resta. Realizado en los 2 primeros números de la pila. Empuja el resultado nuevamente en la pila
  • +: Además. Realizado en los 2 primeros números de la pila. Empuja el resultado nuevamente en la pila
  • ^: Exponenciación. Realizado en los 2 primeros números de la pila. Empuja el resultado nuevamente en la pila
  • %: Modulo. Realizado en los 2 primeros números de la pila. Empuja el resultado nuevamente en la pila
  • !: Factorial. Realizado en el número superior en la pila. Empuja el resultado nuevamente en la pila
  • D: Duplica el número superior en la pila

Operaciones definidas en pseudocódigo:

  • /: push(pop divided by pop)
  • *: push(pop times pop)
  • -: push(pop minus pop)
  • +: push(pop plus pop)
  • ^: push(pop to the pop)
  • %: push(pop mod pop)
  • !: push(factorial pop)
  • D: t = pop; push(t); push(t)

Cómo empujar números a la pila:

Agregar números a la pila es fácil, solo coloque el número sin procesar en su programa donde lo necesite. Si necesita poner varios números en la pila, puede separarlos con una coma ( ,). Su programa no necesitará procesar -números en la entrada. Si el usuario quiere uno, debe presionar el número que quiere negado, cero y -. Los números en la entrada del programa también están restringidos a enteros positivos.

Entrada:

Su programa debe tomar la entrada en la línea de comando, o desde la entrada estándar. La entrada solo consistirá en números (sin notación científica o decimales) delimitados según ,sea ​​necesario, y las operaciones definidas anteriormente.

Salida:

Su programa debe imprimir el número en la parte superior de la pila.

Casos de error:

  • Si el programa intenta sobrecargar la pila, debe imprimir StackUnderflowException!!!.
  • Si tiene división por cero, imprima DivisionByZeroException!!!
  • Si un número que excede los 64 bits, ya sea mientras ejecuta el programa o procesa un número en la entrada, imprima NumberOverflowException!!!
  • Si de alguna manera obtienes un número negativo en la parte superior de la pila y necesitas hacer un factorial, imprime NegativeFactorialException!!!
  • Si tiene un número de coma flotante en la parte superior de la pila y la siguiente operación es factorial, imprima FloatingFactorialException!!!
  • Si no hay números en la pila cuando el programa sale (es decir, el programa estaba vacío) imprimir EmptyProgram!!!

Notas:

  • Toda salida de error debe tener su error estándar o el equivalente más cercano.
  • Todos los números están restringidos a coma flotante de 64 bits.

Programas de ejemplo:

50,47*                 -> 2350
50,47/                 -> 0.94
100,8!                 -> 40320  
100D*                  -> 10000
!                      -> StackUnderflowException!!!
5,2/!                  -> FloatingFactorialException!!!  
4,3!2*/                -> 3 
654,489,48,43/5*7D+-*% -> 77.68749999999909
                       -> EmptyProgram!!!

(Puedo agregar más si es necesario)

J Atkin
fuente
3
Si no fuera por los casos de error, Vitsy podría hacer esto naturalmente (excepto la conversión !a F).
Addison Crump
Pensé, eso es en parte por qué los incluí.
J Atkin
3
El tuyo es algo más amplio en alcance, aunque puede ser discutible que sea un duplicado: codegolf.stackexchange.com/questions/221/…
Digital Trauma
Wow, me olvidé de eso. Pero no creo que sean engañados, ya que hay que procesar errores y se definen más operadores en los míos.
J Atkin
654,489,48,43/5*7D+-*%Debería volver 77.6875. ( 43/48*5-(7+7)debería ser (7+7)-43/48*5)
user81655

Respuestas:

4

Ruby, 412 410 404 392 380 377 caracteres

def e m,x='Exception';warn m+x+?!*3;exit;end
def o;e'StackUnderflow'if$*==[];$*.pop;end
u=->n{e'DivisionByZero'if n.infinite?;e'NumberOverflow'if n>2**64;$*<<n}
f=->n{e'NegativeFactorial'if n<0;e'FloatingFactorial'if n.to_i<n;n<2?1:f[n-1]*n}
gets.gsub(/(\d+)|([+*\/%^-])|(!)|D/){$1?u[$1.to_f]:$2?u[eval"o#{$2>?A?:**:$2}o"]:$3?u[f[o]]:u[x=o]+u[x]}
e'EmptyProgram',''if$*==[]
p o

Esta es una versión de precisión regular usando Float. La precisión del resultado es como en el código de muestra, pero la detección numérica de desbordamiento no es exacta.

Ejecución de muestra:

bash-4.3$ ruby StackyMath.rb <<< '654,489,48,43/5*7D+-*%'
77.68749999999909

Ruby, 378 377 caracteres

def e m,x='Exception';warn m+x+?!*3;exit;end
def o;e'StackUnderflow'if$*==[];$*.pop;end
u=->n{e'NumberOverflow'if n>2**64;$*<<n}
f=->n{e'NegativeFactorial'if n<0;e'FloatingFactorial'if n.to_i<n;n<2?1:f[n-1]*n}
gets.gsub(/(\d+)|([+*\/%^-])|(!)|D/){$1?u[Rational$1]:$2?u[eval"o#{$2>?A?:**:$2}o"]:$3?u[f[o]]:u[x=o]+u[x]}rescue e'DivisionByZero'
e'EmptyProgram',''if$*==[]
p o.to_f

Esta es una versión de alta precisión utilizando Rational. La precisión del resultado no siempre es la misma que en el código de muestra, pero la detección numérica de desbordamiento es exacta.

Ejecución de muestra:

bash-4.3$ ruby StackyMath-hi.rb <<< '654,489,48,43/5*7D+-*%'
77.6875
hombre trabajando
fuente
3

JavaScript (ES6), 430 bytes

422 bytes con ES7 cambiando Math.pow(2,2)a2**2

e=m=>{throw alert(m)};u=prompt();u?alert(eval('u.match(/\\d+|[^,]/g).map(o=>s.push(t=o=="/"?(b=p(a=2))?a/b:e`DivisionByZero43*"?2*23-"?2-23+"?2+23%"?2%23^"?Math.pow(2,2)3D"?s.push(r=2)&&r3!"?eval("for(r=i=2;i<0?e`Negative54:i%1?e`Floating54:--i;)r*=i;r"):+o)&&t==Infinity&&e`NumberOverflow4,s=[],p=_=>s.length?s.pop():e`StackUnderflow4);t'.replace(/[2-5]/g,x=>[,,'p()',':o=="','Exception!!!`','Factorial'][x]))):e`EmptyProgram!!!`

Explicación

Usos evalpara reemplazar ciertas frases comunes. Sin golf y sin el evalse ve así:

e=m=>{throw alert(m)};                           // e = throw error, alert displays
                                                 //     message, throw stops execution
u=prompt();                                      // u = received input
u?alert(                                         // display the result
  u.match(/\d+|[^,]/g)                           // get array of numbers and operators
    .map(o=>                                     // iterate over operators
      s.push(t=                                  // t = last pushed value

        // Execute operator
        o=="/"?(b=p(a=p()))?a/b:                 // make sure argument B is not 0
          e`DivisionByZeroException!!!`:
        o=="*"?p()*p():
        o=="-"?p()-p():
        o=="+"?p()+p():
        o=="%"?p()%p():
        o=="^"?Math.pow(p(),p()):
        o=="D"?s.push(r=p())&&r:
        o=="!"?eval("                            // eval to enable for loop in ternary
          for(                                   // no factorial in JS so do this manually
            r=i=p();
            i<0?e`NegativeFactorialException!!!` // check for errors
              :i%1?
                e`FloatingFactorialException!!!`
                :--i;
          )
            r*=i;
          r"):                                   // return r
        +o                                       // if not an operator cast as a number
      )&&t==Infinity&&                           // JS turns anything over 64 bit float
        e`NumberOverflowException!!!`,           //     max value into Infinity
      s=[],                                      // s = stack array
      p=_=>s.length?s.pop():                     // p = check stack then pop
        e`StackUnderflowException!!!`
    )&&t                                         // return top stack element
  ):e`EmptyProgram!!!`                           // error if input length is 0
usuario81655
fuente
Si desea actualizar a ES7, se puede utilizar el operador de exponenciación ES7 reemplazar Math.pow(p(),p())con p()**p().
usandfriends
1
@usandfriends Estaba pensando en ello, pero significaba que no funcionaría en mi navegador, así que lo dejé fuera. : P Agregaré una nota diciendo eso.
user81655
1

Groovy, 718 bytes. ¡Delantero!

También podría publicar mi impl golf. Conoce mi gran muro de código:

g='Exception!!!'
a={System.err.print(it);System.exit(1)}
b=new Stack()
c={b.push(it)}
v=2d**64d
d={b.pop()}
e={if(b.size()<it)a('StackUnderflow'+g)}
f={a('NumberOverflow'+g)}
h={e(2)
c(Eval.xy(d(),d(),"x$it y"))
if(b.peek()>v)f()}
k={i->if(i<0)a('NegativeFactorial'+g)
if(Math.abs(i-(i as long))>1E-6)a('FloatingFactorial'+g)
(2..i).inject{x,y->(v/x<y)?f():x*y}}
l=['/':{e(2)
m=d()
n=d()
if(n==0)a('DivisionByZero'+g)
c(m/n)},'!':{e(1)
c(k(d()))},'D':{e(1)
c(b.peek())}]
System.in.newReader().readLine().findAll(~/\d+|[^,]/).each{x->if(x.matches(/\d+/))try{c(x as double)}catch(Exception e){f()}
else if("+-*%^".contains(x))h(x.replace('^','**'))
else l[x]()}
if(b){o=d()
if(Double.isInfinite(o))f()
println o}else a('EmptyProgram!!!')

Sin golf:

error = {System.err.print(it);System.exit(1)}

stack = new Stack()
maxVal = 2d**64d

push = {stack.push(it)}
pop = {stack.pop()}

checkAtLeast = {if (stack.size() < it) error('StackUnderflow'+exception)}
numberOverflow = {error('NumberOverflow'+exception)}

exception = 'Exception!!!'

def dArgOp(i) {
    checkAtLeast(2)
    push(Eval.xy(pop(), pop(), "x$i y"))
    if(stack.peek() > maxVal) numberOverflow
}

factorial = {i->
    if (i < 0)
        error('NegativeFactorial'+exception)
    if (Math.abs(i - (i as long)) > 1E-6)
        error('FloatingFactorial'+exception)
    (2..i).inject {acc, it ->
        (maxVal/acc < it)?numberOverflow():acc*it
    }
}

ops = [
'/' : {
    checkAtLeast(2)
    first = pop()
    second = pop()
    if (second == 0)
        error('DivisionByZero'+exception)
    push(first / second)
},
'!' : {
    checkAtLeast(1)
    push(factorial(pop()))
},
'D' : {
    checkAtLeast(1)
    push(stack.peek())
}]

input = System.in.newReader().readLine()
tokens = input.findAll(~/\d+|[^,]/)

tokens.each {
    print "current token: $it  \t stack before eval: $stack "
    if (it.matches(/\d+/))
        try {
            push(it as double)
        } catch (Exception e) {
            numberOverflow()
        }

    else if ("+-*%^".contains(it))
        dArgOp(it.replace('^','**'))
    else
        ops[it]()
    println "result: ${stack.peek()}"
}

if (stack) {
    top = pop()
    if (Double.isInfinite(top))
        numberOverflow()
    println top
} else
    error('EmptyProgram!!!')

Edición 1: ahorre ~ 15 bytes gracias a @Doorknob
Edición 2: elimine ~ 130 bytes con algunos trucos más

J Atkin
fuente
No conozco a Groovy, pero parece que tienes muchos espacios en blanco innecesarios. Por ejemplo, alrededor de operadores, después de for/ if, etc.
Pomo de la puerta
Vaya, acabo de notar muchos más lugares para eliminar espacios en blanco. Gracias por el consejo.
J Atkin
Puedes usar en System.in.textlugar de System.in.newReader().readLine().
un spaghetto
Realmente no. .textes codicioso y mientras los datos ingresen en el lector, no volverá.
J Atkin
Correcto, pero esto es código-golf. No es gran cosa si la gente tiene que escribir Control-D después de su entrada.
un espagueti el
1

Caramelo , 298 348 392 bytes

Aunque Candy está basado en la pila, no estoy seguro de que realmente haya ayudado ...

&{|"EmptyProgram!!!\n"(;).}(=bYZay0=zx4k"!%*+,-/D^"(iYe{Z=x})aYb=z=ya=X{Y{cZ0=yza}b212202221(i=ZXe{y})a0=zcY0j{XjZ{|D1b#64R(=c2*)c>{b"NumberOverFlow"(;)i}}|i}aZ{(=)"Exception!!!\n"(;).}0=yz|A#48-Z#10*+=z1=y})c?(=).@0&<{1|b"StackUnderflow"(;)c0}.@1~ALe{A0<{b"Negative"(;)i|1bAR(=cA*)}|b"Floating"(;)i}Z{b"Factorial"(;)}.@2W%.@3*.@4+@5.@6W-.@7WD{/|b"DivisionByZero"(;)i}.@8~A.@9=xK=y=1bA_(cX*).

Formateado un poco revela un poco de estructura:

&{|"EmptyProgram!!!\n"(;).}
(=bYZay0=zx4k
  "!%*+,-/D^"
  (iYe{Z=x})
  aYb=z=ya=X
  {
    Y{cZ0=yza}b
    212202221(i=ZXe{y})
    a0=zcY0j
    {XjZ{|D1b#64R(=c2*)c>{b"NumberOverFlow"(;)i}}|i}
    aZ{(=)"Exception!!!\n"(;).}
    0=yz|A#48-Z#10*+=z1=y
  }
)c?(=).
@0&<{1|b"StackUnderflow"(;)c0}.
@1~ALe{A0<{b"Negative"(;)i|1bAR(=cA*)}|b"Floating"(;)i}Z{"Factorial"(;)}.
@2W%.@3*.@4+@5.@6W-.@7WD{/|"DivisionByZero"(;)i}.@8~A.@9=xK=y=1bA_(cX*).

La matemática real ocurre en las últimas dos líneas. Lo conduce una mesa de salto en la tercera línea.

Dale Johnson
fuente
Dang, veo que me perdí DivisionByZero, NegativeFactorial y Overflow. ¡Solo estaba mirando los casos de prueba!
Dale Johnson
Wow, esto es genial. Tal vez necesite buscar dulces.
J Atkin
Todavía estoy trabajando en cómo definir exactamente el desbordamiento.
Dale Johnson
En realidad tuve el mismo problema en mi respuesta. Vea los comentarios debajo de mi respuesta.
J Atkin
Se corrigió el desbordamiento ahora. Utilicé el mismo enfoque que el Ruby, que es solo para comparar con 2 ^ 64 al final de cada operación.
Dale Johnson