Расширенный калькулятор


28

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

Программа должна принять ввод с использованием стандартного ввода и вывести правильный ответ. Для языков, которые не имеют функций для приема стандартного ввода, вы можете взять на себя функции readLineи printобрабатывать эти задачи.

Требования:

  • Не использует какие-либо "eval" функции
  • Может обрабатывать числа с плавающей запятой и отрицательные числа
  • Поддерживает как минимум операторы +, -, *, / и ^
  • Поддерживает скобки и скобки для переопределения нормального порядка
  • Может обрабатывать ввод, содержащий один или несколько пробелов между операторами и числами
  • Оценивает ввод, используя стандартный порядок операций

Тестовые случаи

вход

10 - 3 + 2

Выход

9


вход

8 + 6 / 3 - 7 + -5 / 2.5

Выход

1


вход

4 + [ ( -3 + 5 ) * 3.5 ] ^ 2 - 12

Выход

41

1
Это нормально, если у выводимых чисел в конце есть трейлинг, .0если они целые? Также: насколько точным должен быть калькулятор (в отношении точности с плавающей запятой и т. Д.)?
sepp2k

1
Выход может иметь трейлинг .0на конце. Я не слишком уверен в точности, но чем больше, тем лучше.
Кевин Браун

1
Версия переполнения стека была оценщиком математических выражений (полная PEMDAS) . Хотя многие из ответов на этот вопрос - подсчет строк (?!?). Еще есть несколько компактных ответов в ц.
dmckee

Бонус за калькуляторы PN / RPN?
Матин Улхак

Ответы:


8

С ++, 640 583

string k="[]()+-*/^";stack<double> m;stack<char> n;
#define C(o,x,y) ('^'==o?x<y:x<=y)
#define Q(a) double a=m.top();m.pop();
#define R(o) {Q(b)Q(a)m.push(o=='+'?a+b:o=='-'?a-b:o=='*'?a*b:o=='/'?a/b:o=='^'?pow(a,b):0);n.pop();}
while(!cin.eof()){string s;getline(cin,s,' ');if(s.empty())continue;if('\n'==*--s.end())s.erase(--s.end());(s.size()==1&&s.npos!=k.find(s[0]))?({char c=s[0]=='['?'(':s[0]==']'?')':s[0];while(!n.empty()&&'('!= c&&C(c,k.find(c),k.find(n.top())))R(n.top());')'==c?n.pop():n.push(c);}):m.push(strtod(s.c_str(),0));}while(!n.empty())R(n.top());cout<<m.top()<<endl;

изрезанный

string k="[]()+-*/^";
stack<double> m;
stack<char> n;
#define C(o,x,y) ('^'==o?x<y:x<=y)
#define Q(a) double a=m.top();m.pop();
#define R(o) {Q(b)Q(a)m.push(o=='+'?a+b:o=='-'?a-b:o=='*'?a*b:o=='/'?a/b:o=='^'?pow(a,b):0);n.pop();}
while(!cin.eof())
{
    string s;
    getline(cin,s,' ');
    if(s.empty())continue;
    if('\n'==*--s.end())s.erase(--s.end());
    (s.size()==1&&s.npos!=k.find(s[0]))?({
        char c=s[0]=='['?'(':s[0]==']'?')':s[0];
        while(!n.empty()&&'('!= c&&C(c,k.find(c),k.find(n.top())))
            R(n.top());
        ')'==c?n.pop():n.push(c);
    }):m.push(strtod(s.c_str(),0));
}
while(!n.empty())
    R(n.top());
cout<<m.top()<<endl;

Мой первый код гольф, так что жду комментариев и критики!


Обрабатывает правую ассоциативность оператора возведения в степень, что, по-видимому, не представляется решением Perl от JB.
drspod

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

1
+1 красиво играли в гольф, но ... просто падение включает, using namespace stdи основная функция не очень хорошо, не так ли?
перестал поворачиваться против часовой стрелки

2

PHP - 394 354 312 символов

<?=e(!$s=preg_split('#\s+#',`cat`,-1,1),$s);function e($P,&$s){$S='array_shift';if(($a=$S($s))=='('|$a=='['){$a=e(0,$s);$S($s);}while($s&&($p=strpos(' +-*/^',$o=$s[0]))&&$p>=$P){$b=e($p+($S($s)!='^'),$s);if($o=='+')$a+=$b;if($o=='-')$a-=$b;if($o=='*')$a*=$b;if($o=='/')$a/=$b;if($o=='^')$a=pow($a,$b);}return$a;}

Отступ:

<?
preg_match_all('#\d+(\.\d+)?|\S#',`cat`,$m);
$s=$m[0];
function e($P) {
        global $s;
        if (strpos(" ([",$s[0])){
                array_shift($s);
                $a=e(0);
                array_shift($s);
        } else {
                $a=array_shift($s);
                if ($a=='-')$a.=array_shift($s);
        }
        while ($s && ($p=strpos(' +-*/^',$o=$s[0])) && $p >= $P) {
                array_shift($s);
                $b = e($p+($o!='^'));
                switch($o){
                case'+':$a+=$b;break;
                case'-':$a-=$b;break;
                case'*':$a*=$b;break;
                case'/':$a/=$b;break;
                case'^':$a=pow($a,$b);
                }
        }
        return $a;
}
echo e(0);

2

Постскриптум, 446

Это использует алгоритм маневрового двора.

[/*[/p
2/e{mul}>>/d[/p
2/e{div}>>/+[/p
1/e{add}>>/-[/p
1/e{sub}>>/o[/p
9/e{}>>/c[/p
-1/e{}>>/^[/p
3/e{exp}>>/p
0>>begin/s(%stdin)(r)file 999 string readline pop def
0 1 s length 1 sub{s exch[0 1 255{}for]dup[(\(o)([o)(\)c)(]c)(/d)]{{}forall
put dup}forall
pop
3 copy pop
get
get
put}for{s token not{exit}if
exch/s exch store{cvr}stopped{load
dup/p get
p
le{currentdict end
exch begin/e get exec}{begin}ifelse}if}loop{{e end}stopped{exit}if}loop
=

Без гольфа и прокомментировал:

% We associate the operators with their precedence /p and the executed commend /e
[
  (*)[/p  2 /e{mul}>>
  (d)[/p  2 /e{div}>> % This is division
  (+)[/p  1 /e{add}>>
  (-)[/p  1 /e{sub}>>
  (o)[/p  9 /e{   }>> % This is open bracket
  (c)[/p -1 /e{   }>> % This is close bracket
  (^)[/p  3 /e{exp}>>
  /p 0
>>begin

% Let's read the input string
/s(%stdin)(r)file 999 string readline pop def

% If we want to use the token operator, we have to replace (, [, ), ] and / to get meaningful results
% We use kind of an encoding array (familiar to PostScripters) to map those codes to o, c, and d.
0 1 s length 1 sub{        % index
  s exch                   % string index
  [0 1 255{}for] dup       % string index translationArray translationArray
  [(\(o)  ([o)  (\)c)  (]c)  (/d)] % string index translationArray translationArray reencodeArray
  {                        % string index translationArray translationArray translationString
    {}forall               % string index translationArray translationArray charCode newCharCode
    put dup                % string index translationArray translationArray
  }forall                  % string index translationArray translationArray
  pop                      % string index translationArray
  3 copy pop               % string index translationArray string index
  get                      % string index translationArray charCode
  get                      % string index translatedCharCode
  put                      % -/-
}for

% Now we can actually start interpreting the string
% We use the stack for storing numbers we read and the dictionary stack for operators that are "waiting"
{                          % number*
  s token not{exit}if      % number* string token
  exch /s exch store       % number* token
  % We try to interpret the token as a number
  {cvr}stopped{            % number* token
    % If interpretation as number fails, we have an operator
    load                   % number* opDict
    % Compare operator precedence with last operator on dictstack
    dup /p get             % number* opDict opPrec
    p                      % number* opDict opPrec prevOpPrec
    le {                   % number* opDict
      % If the last operator on the stack has at least the same precedence, execute it
      currentdict end      % number* opDict prevOpDict
      exch begin           % number* prevOpDict
      /e get exec          % number*
    }{                     % number* opDict
      % If last operator doesn't have higher precedence, put the new operator on the dictstack as well
      begin
    }ifelse
  }if
}loop
% If we're finished with interpreting the string, execute all operators that are left on the dictstack
{{e end}stopped{exit}if}loop
=

TODO : право-ассоциативность возведения в степень


Ах ... Диктофак: блестящий!
luser droog

Мне разрешено комментировать stackoverflow, поэтому я немного растерялся. Это нормально, что репутация управляется отдельно, или я испортил свои логины?
Томас В.

Я хотел сказать вам, что с вашим решением должно быть что-то не так, потому что все три тестовых примера, приведенные выше, дают сбой. Тем не менее, я еще не пытался понять, что вы делаете (некоторые комментарии были бы классными ;-)).
Томас В.

1) Как только вы нажмете 200 на любом сайте, вы начнете с 101 на каждом сайте. Или нажмите 50 здесь. 2) Аааа! Я думал, что это было быстрое расширение базового калькулятора. Я даже не видел, что скобки требуются! И я не очень хорошо это проверял. Ну что ж; брюки вниз, по крайней мере, мои трусы чистые!
Люсер Дрог

@luserdroog: "@ThomasW" не приглашает меня комментировать.
Томас В.

1

Python 2 , 339 335 байт

import re
x,s=input(),re.sub
def f(y):
 y,r=s('- ','+ -',y).split(),['^','*','/','+','-']
 for c in r:
  while c in y:d=y.index(c)-1;a,b=map(float,[y[d],y[d+2]]);y=y[:d]+[((a,-a)[a<0]**b,a*b,a/b,a+b,a-b)[r.index(c)]]+y[d+3:]
 return`y[0]`
w=lambda b:s("[([]+[\d+\-*/^ .]*[)\]]",lambda m:f(m.group()[1:]),s(' +',' ',b))
print f(w(w(x)))

Попробуйте онлайн!

  • -4 байта путем изменения str (x) с помощью обратных кавычек ``!

0

Постскриптум, 1000 695 665 494

Украл идеи от ThomasW. Добавлена ​​функция: принимает строки с или без пробелов вокруг операторов.[функция удалена]


Использование ARGUMENTSкороче %stdin, и проще для тестирования, чтобы загрузить!


Упростил замену, чтобы просто заменить скобки на скобки.

575(1)10:36 PM:ps 0> gsnd -q -- calc2bg.ps '10 - 3 + 2'
9
576(1)10:37 PM:ps 0> gsnd -q -- calc2bg.ps '8 + 6 / 3 - 7 + -5 / 2.5'
1.0
577(1)10:37 PM:ps 0> gsnd -q -- calc2bg.ps '4 + [ ( -3 + 5 ) * 3.5 ] ^ 2 - 12'
41.0

Код:

/T[/^[/C{exp}/P 4/X{le}>>/*[/C{mul}/P 3/X{lt}>>/[/C{div}/P
3/X{lt}>>/+[/C{add}/P 2/X{lt}>>/-[/C{sub}/P
2/X{lt}>>>>def[/integertype{}/realtype{}/stringtype{V}/nametype{cvlit/N
exch store{P T N get dup/P get exch/X get exec{exit}if C end}loop T N get
begin}91 40 93 41>>begin/V{0 1 2 index length 1 sub{2 copy get
dup where{exch get}if 3 copy put pop pop}for[/N 0/R 0/P 0/C{}>>begin{token
not{exit}if exch/R exch store dup type exec R}loop{P 0 eq{end exit}if C
end}loop}def ARGUMENTS{V ==}forall

Развернулся и прокомментировал:

%!
%Shunting-Yard Algorithm using dictstack for operators
%invoke with %gsnd -q -- calc2bg.ps [ 'expr1' ]*

%The operator table. C:code P:precedence X:test(implements associativity)
/T[
    /^[/C{exp}/P 4/X{le}>>
    /*[/C{mul}/P 3/X{lt}>>
    /[/C{div}/P 3/X{lt}>>
    /+[/C{add}/P 2/X{lt}>>
    /-[/C{sub}/P 2/X{lt}>>
>>def

%The type-dispatch dictionary
%numbers: do nothing
%string: recurse
%name: process op
[%/integertype{}/realtype{} %now uses `where` below
/stringtype{V}/nametype{
pstack()=
    cvlit/N exch store %stash cur-op
    {
        P %prec(tos)
        T N get %prec(tos) cur-op-dict
        dup/P get %prec(tos) cur-op-dict prec(cur-op)
        exch/X get %prec(tos) prec(cur-op) test(cur-op)
        exec{exit}if %exit if prec(tos) < || <= prec(cur-op)
/C load ==
        C %pop-and-apply
        end
pstack()=
    } loop
    T N get begin %push cur-op
}>>begin

%substitutions
[91 40 93 41>>begin %replace brackets with parens
/V {
    %pre-process
    0 1 2 index length 1 sub {
        2 copy get
        dup where { exch get } if
        3 copy put pop pop
    } for
dup ==

    [/N 0/R 0/P 0/C{}>>begin %dummy base operator and storage
    { token not{exit}if exch /R exch store %extract token, stash Remainder
pstack(>)=
        %dispatch type procedure
        dup type dup where { pop exec }{ pop } ifelse
    R }loop
pstack()=
    {
        P 0 eq{end exit}if %hit dummy op: exit
/C load ==
        C end %pop and apply
    } loop
} def

ARGUMENTS{V ==}forall %iterate through the command-line arguments

@ThomasW Интересно, работает ли это, чтобы пригласить вас оставить комментарий? (?)
luser droog

Я опубликовал более полную реализацию той же идеи в comp.lang.postscript .
Люсер Дрог
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.