Интерпретировать StackyMath!

14

Пора вам реализовать мой новый язык на основе стека! Это называется StackyMath. Это будет основанный на стеке язык с 8 операциями над стеком и способами добавления чисел в стек.

Список операций:

  • /: Отдел. Выполняется 2 верхних номера стека. Откладывает результат обратно в стек.
  • *Умножение. Выполняется 2 верхних номера стека. Откладывает результат обратно в стек
  • -: Вычитание. Выполняется 2 верхних номера стека. Откладывает результат обратно в стек
  • +Дополнение Выполняется 2 верхних номера стека. Откладывает результат обратно в стек
  • ^: Экспонирование. Выполняется 2 верхних номера стека. Откладывает результат обратно в стек
  • %: По модулю. Выполняется 2 верхних номера стека. Откладывает результат обратно в стек
  • !Факториал. Выполняется по верхнему номеру в стеке. Откладывает результат обратно в стек
  • D: Дублировать верхний номер в стеке

Операции, определенные в псевдокоде:

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

Как поместить числа в стек:

Добавить цифры в стек просто, просто поместите необработанный номер в свою программу там, где он вам нужен. Если вам нужно поместить несколько чисел в стек, вы можете разделить их запятой ( ,). Ваша программа не должна будет обрабатывать -числа во входных данных. Если пользователь хочет один, он должен нажать на число, которое он хочет отрицать, ноль и -. Числа на входе программы также ограничены положительными целыми числами.

Входные данные:

Ваша программа должна принимать данные из командной строки или из стандартного ввода. Ввод будет состоять только из чисел (без научных обозначений или десятичных дробей), разделенных , необходимости, и из операций, определенных выше.

Выход:

Ваша программа должна напечатать число на вершине стека.

Случаи ошибки:

  • Если программа пытается перегрузить стек, вы должны распечатать StackUnderflowException!!!.
  • Если у вас деление на ноль, выведите DivisionByZeroException!!!
  • Если число превышает 64 бита, либо во время выполнения программы, либо при обработке числа на входе, выведите NumberOverflowException!!!
  • Если каким-то образом вы получили отрицательное число на вершине стека, и вам нужно сделать факториал, выведите NegativeFactorialException!!!
  • Если у вас есть число с плавающей запятой на вершинах стека, и следующая операция будет факториальной, выведите FloatingFactorialException!!!
  • Если при выходе из программы в стеке нет чисел (т. Е. Программа была пуста), выведите EmptyProgram!!!

Примечания:

  • Все выходные данные об ошибках должны получить либо ошибку, либо ближайший эквивалент.
  • Все числа ограничены 64-битной плавающей точкой.

Примеры программ:

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

(Я могу добавить больше, если это необходимо)

Дж Аткин
источник
3
Если бы не случаи ошибки, Витси мог бы сделать это естественным путем (кроме преобразования !в F).
Эддисон Крамп
Я решил, что отчасти поэтому я включил их.
Дж Аткин
3
У вас несколько более широкий охват, хотя можно утверждать, что это дубликат: codegolf.stackexchange.com/questions/221/…
Цифровая травма
Вау, я забыл об этом. Но я не думаю, что они являются обманщиками, так как вы должны обрабатывать ошибки, и в моем распоряжении больше операторов.
Дж Аткин
654,489,48,43/5*7D+-*%должен вернуться 77.6875. ( 43/48*5-(7+7)должно быть (7+7)-43/48*5)
user81655

Ответы:

4

Рубин, 412 410 404 392 380 377 символов

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

Это обычная прецизионная версия с использованием Float. Точность результата такая же, как в примере кода, но обнаружение переполнения чисел не является точным.

Образец прогона:

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

Рубин, 378 377 символов

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

Это высокоточная версия с использованием Rational . Точность результата не всегда такая же, как в примере кода, но точное определение числового переполнения.

Образец прогона:

bash-4.3$ ruby StackyMath-hi.rb <<< '654,489,48,43/5*7D+-*%'
77.6875
manatwork
источник
3

JavaScript (ES6), 430 байт

422 байта с ES7, изменив 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!!!`

объяснение

Используется evalдля замены некоторых общих фраз. Без разгула и без evalнего это выглядит так:

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
источник
Если вы хотите перейти на ES7, вы можете использовать оператор возведения в степень ES7 для замены Math.pow(p(),p())на p()**p().
usandfriends
1
@usandfriends Я думал об этом, но это означало, что это не будет работать в моем браузере, поэтому я оставил это. : P Я добавлю записку, говорящую это.
user81655
1

Groovy, 718 байт. Fore!

Можно также опубликовать мой импл гольф. Встречайте мою большую стену кода:

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

Ungolfed:

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

Редактировать 1: сэкономить ~ 15 байт благодаря @Doorknob
Редактировать 2: сбросить ~ 130 байтов с помощью нескольких хитростей

Дж Аткин
источник
Я не знаю Groovy, но у вас, похоже, много лишних пробелов. Например, вокруг операторов, после for/ ifи т. Д.
дверная ручка
Ой, просто заметил еще много мест для удаления пробелов. Спасибо за чаевые.
Дж Аткин
Вы можете использовать System.in.textвместо System.in.newReader().readLine().
спагетто
На самом деле, нет. .textявляется жадным и пока данные в считывателе не возвращаются.
Дж Аткин
Да, но это код-гольф. Это не имеет большого значения, если люди вводят Control-D после их ввода.
спагетто
1

Конфеты , 298 348 392 байта

Хотя Candy основана на стеке, я не уверен, что это действительно помогло ...

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

Немного отформатированный показывает немного структуры:

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

Фактическая математика происходит в последних двух строках. Его ведет там таблица прыжков на третьей строке.

Дейл Джонсон
источник
Черт, я вижу, что я пропустил DivisionByZero, NegativeFactorial и Overflow. Я просто смотрю на тестовые случаи!
Дейл Джонсон
Вау, это круто Мне просто нужно поискать конфеты.
Дж Аткин
Я все еще работаю над тем, как точно определить переполнение.
Дейл Джонсон
На самом деле у меня была та же проблема в моем ответе. Смотрите комментарии ниже моего ответа.
Дж Аткин
Исправлено переполнение сейчас. Я использовал тот же подход, что и Ruby, который просто сравнивается с 2 ^ 64 в конце каждой операции.
Дейл Джонсон