Interprétez StackyMath!

14

Il est temps pour vous d'implémenter mon nouveau langage basé sur la pile! Cela s'appelle StackyMath. Ce sera un langage basé sur la pile avec 8 opérations sur la pile et des façons d'ajouter des numéros à la pile.

Liste des opérations:

  • /: Division. Joué sur les 2 premiers numéros de la pile. Repousse le résultat sur la pile.
  • *: Multiplication. Joué sur les 2 premiers numéros de la pile. Repousse le résultat sur la pile
  • -: Soustraction. Joué sur les 2 premiers numéros de la pile. Repousse le résultat sur la pile
  • +: Une addition. Joué sur les 2 premiers numéros de la pile. Repousse le résultat sur la pile
  • ^: Exponentiation. Joué sur les 2 premiers numéros de la pile. Repousse le résultat sur la pile
  • %: Modulo. Joué sur les 2 premiers numéros de la pile. Repousse le résultat sur la pile
  • !: Factorielle. Joué sur le numéro du haut de la pile. Repousse le résultat sur la pile
  • D: Dupliquer le premier numéro de la pile

Opérations définies en pseudo code:

  • /: 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)

Comment envoyer des nombres à la pile:

Ajouter des numéros à la pile est facile, il suffit de mettre le numéro brut dans votre programme là où vous en avez besoin. Si vous devez mettre plusieurs nombres sur la pile, vous pouvez les séparer par une virgule ( ,). Votre programme n'aura pas besoin de traiter les -nombres en entrée. Si l'utilisateur en veut un, il doit pousser le nombre qu'il veut annuler, zéro et -. Les nombres dans l'entrée du programme sont également limités à des nombres entiers positifs.

Contribution:

Votre programme doit prendre l'entrée sur la ligne de commande, ou à partir de std in. L'entrée sera uniquement composée de nombres (pas de notation scientifique ou décimales) délimités par ,au besoin, et des opérations définies ci-dessus.

Production:

Votre programme doit imprimer le numéro en haut de la pile.

Cas d'erreur:

  • Si le programme essaie de surcharger la pile, vous devez imprimer StackUnderflowException!!!.
  • Si vous avez une division par zéro, imprimez DivisionByZeroException!!!
  • Si un nombre qui dépasse 64 bits, lors de l'exécution du programme ou du traitement d'un nombre dans l'entrée, imprimez NumberOverflowException!!!
  • Si vous obtenez en quelque sorte un nombre négatif en haut de la pile et que vous devez faire une factorielle, imprimez NegativeFactorialException!!!
  • Si vous avez un nombre à virgule flottante sur les sommets de la pile et que l'opération suivante est factorielle, imprimez FloatingFactorialException!!!
  • Si aucun numéro ne se trouve sur la pile à la fin du programme (c'est-à-dire que le programme était vide) imprimer EmptyProgram!!!

Remarques:

  • Toutes les sorties d'erreur vers devraient avoir yo std err ou l'équivalent le plus proche.
  • Tous les nombres sont limités à une virgule flottante 64 bits.

Exemples de programmes:

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

(Je peux ajouter plus si nécessaire)

J Atkin
la source
3
S'il n'y avait pas les cas d'erreur, Vitsy pourrait le faire naturellement (sauf la conversion !en F).
Addison Crump
J'ai pensé, c'est en partie pourquoi je les ai inclus.
J Atkin
3
Le vôtre est un peu plus large, bien qu'il puisse être discutable qu'il s'agit d'un doublon: codegolf.stackexchange.com/questions/221/…
Digital Trauma
Wow, j'ai oublié celui-là. Mais je ne pense pas que ce soient des dupes car il faut traiter les erreurs et plus d'opérateurs sont définis dans le mien.
J Atkin
654,489,48,43/5*7D+-*%devrait revenir 77.6875. ( 43/48*5-(7+7)devrait être (7+7)-43/48*5)
user81655

Réponses:

4

Rubis, 412 410 404 392 380 377 caractères

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

Il s'agit d'une version de précision régulière utilisant Float. La précision du résultat est comme dans l'exemple de code, mais la détection de débordement numérique n'est pas exacte.

Exemple d'exécution:

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

Rubis, 378 377 caractères

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

Il s'agit d'une version de haute précision utilisant Rational. La précision du résultat n'est pas toujours la même que dans l'exemple de code, mais la détection de dépassement numérique est exacte.

Exemple d'exécution:

bash-4.3$ ruby StackyMath-hi.rb <<< '654,489,48,43/5*7D+-*%'
77.6875
homme au travail
la source
3

JavaScript (ES6), 430 octets

422 octets avec ES7 en passant Math.pow(2,2)à2**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!!!`

Explication

Utilise evalpour remplacer certaines phrases courantes. Non golfé et sans le evalça ressemble à ceci:

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
user81655
la source
Si vous souhaitez effectuer une mise à niveau vers ES7, vous pouvez utiliser l' opérateur d'exponentiation ES7 pour le remplacer Math.pow(p(),p())par p()**p().
usandfriends
1
@usandfriends J'y pensais, mais cela signifiait que cela ne fonctionnerait pas dans mon navigateur, donc je l'ai laissé de côté. : P Je vais ajouter une note disant cela.
user81655
1

Groovy, 718 octets. Fore!

Peut aussi poster mon implémenté au golf. Rencontrez mon grand mur de code:

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!!!')

Non 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!!!')

Édition 1: économisez ~ 15 octets grâce à @Doorknob
Édition 2: supprimez ~ 130 octets avec quelques astuces supplémentaires

J Atkin
la source
Je ne connais pas Groovy, mais vous semblez avoir beaucoup d'espaces inutiles. Par exemple, autour des opérateurs, après for/ if, etc.
Poignée de porte
Oups, vient de remarquer de nombreux autres endroits pour supprimer les espaces blancs. Merci pour le conseil.
J Atkin
Vous pouvez utiliser à la System.in.textplace de System.in.newReader().readLine().
un spaghetto du
Pas vraiment. .textest gourmand et tant que les données sont dans le lecteur, il ne reviendra pas.
J Atkin
C'est vrai, mais c'est du code-golf. Ce n'est pas un gros problème si les gens doivent taper Control-D après leur entrée.
un spaghetto du
1

Candy , 298 348 392 octets

Bien que Candy soit basée sur la pile, je ne suis pas sûr que cela ait vraiment aidé ...

&{|"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*).

Un peu formaté révèle un peu de structure:

&{|"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*).

Le calcul réel se produit sur les deux dernières lignes. Il y est entraîné par une table de saut sur la troisième ligne.

Dale Johnson
la source
Dang, je vois que j'ai raté DivisionByZero, NegativeFactorial et Overflow. Je regardais juste les cas de test!
Dale Johnson
Wow, c'est cool. J'ai peut-être juste besoin de chercher des bonbons.
J Atkin
Je travaille toujours sur la façon de définir exactement le débordement.
Dale Johnson
En fait, j'ai eu le même problème dans ma réponse. Voir les commentaires ci-dessous ma réponse.
J Atkin
Correction du débordement maintenant. J'ai utilisé la même approche que le Ruby, qui est juste à comparer à 2 ^ 64 à la fin de chaque opération.
Dale Johnson