Kalkulator Lanjutan

28

Anda harus menulis program yang mengevaluasi string yang akan dimasukkan ke dalam kalkulator tingkat lanjut.

Program harus menerima input menggunakan stdin dan menampilkan jawaban yang benar. Untuk bahasa yang tidak memiliki fungsi untuk menerima stdin, Anda dapat mengambil fungsi readLinedan printuntuk menangani tugas ini.

Persyaratan:

  • Tidak menggunakan segala jenis fungsi "eval"
  • Dapat menangani floating point dan angka negatif
  • Mendukung setidaknya operator +, -, *, /, dan ^
  • Mendukung tanda kurung dan tanda kurung untuk mengganti urutan normal
  • Dapat menangani input yang berisi satu atau beberapa ruang antara operator dan angka
  • Mengevaluasi input menggunakan urutan operasi standar

Uji Kasus

Memasukkan

10 - 3 + 2

Keluaran

9


Memasukkan

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

Keluaran

1


Memasukkan

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

Keluaran

41
Kevin Brown
sumber
1
Apakah boleh jika angka-angka yang dikeluarkan memiliki tanda .0akhir jika mereka bilangan bulat? Juga: seberapa akurat kalkulator harus (mengenai presisi floating point dan semacamnya)?
sepp2k
1
Output dapat memiliki trailing .0di ujungnya. Saya tidak terlalu yakin tentang presisi, tetapi lebih banyak lebih baik.
Kevin Brown
1
Versi Stack Overflow adalah evaluator ekspresi Matematika (PEMDAS penuh) . Padahal banyak jawaban untuk yang satu itu menghitung garis (?!?). Masih ada beberapa jawaban ringkas dalam c.
dmckee
Bonus untuk kalkulator PN / RPN?
Mateen Ulhaq

Jawaban:

8

C ++, 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;

Bertakuk

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;

Golf kode pertama saya, sangat menantikan komentar & kritik!

drspod
sumber
Menangani asosiasi-hak yang tepat dari operator eksponensial, yang tampaknya tidak solusi JB's.
drspod
Pernyataan masalah atau halaman wikipedia tertaut tidak menyebutkan eksponensial yang harus diasosiasikan dengan benar. Lebih jauh, halaman wikipedia dengan jelas mengatakan kedua cara tersebut dapat ditemukan pada kalkulator komersial.
JB
1
+1 golf dengan baik, tapi ... hanya menjatuhkan termasuk, using namespace stddan fungsi utama tidak terlalu oke, bukan?
Berhenti menghidupkan counterclock dengan
2

PHP - 394 354 312 karakter

<?=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;}

Bertakuk:

<?
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);
Arnaud Le Blanc
sumber
2

Catatan tambahan, 446

Ini menggunakan algoritma halaman shunting.

[/*[/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
=

Berhenti bermain golf dan berkomentar:

% 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 : Asosiatif -kanan eksponen

Thomas W.
sumber
Ah ... Dictstack: brilian!
luser droog
Saya diizinkan mengomentari stackoverflow, jadi saya agak bingung. Apakah normal bahwa reputasi dikelola secara terpisah, atau apakah saya mengacaukan login saya?
Thomas W.
Saya ingin memberi tahu Anda bahwa pasti ada yang salah dengan solusi Anda karena ketiga uji yang diberikan di atas gagal. Namun, saya belum mencoba memahami apa yang Anda lakukan (beberapa komentar akan keren ;-)).
Thomas W.
1) Setelah Anda menekan 200 di situs mana pun, Anda akan mulai dari 101 di setiap situs. Atau tekan 50 di sini. 2) AUGHH! Saya pikir itu adalah perpanjangan cepat dari Kalkulator Dasar. Saya bahkan tidak melihat tanda kurung diperlukan! Dan saya tidak mengujinya dengan baik. Baiklah; celana panjang, setidaknya celana saya bersih!
luser droog
@luserdroog: "@ThomasW" tidak mengundang saya untuk berkomentar.
Thomas W.
1

Python 2 , 339 335 byte

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

Cobalah online!

  • -4 byte dengan mengubah str (x) dengan backticks ``!
Keerthana Prabhakaran
sumber
0

Catatan tambahan, 1000 695 665 494

Mencuri ide dari ThomasW. Fitur yang ditambahkan: menerima string dengan atau tanpa spasi di sekitar operator.[fitur dihapus]


Menggunakan ARGUMENTSlebih pendek daripada %stdin, dan lebih mudah untuk menguji, untuk boot!


Substitusi yang disederhanakan untuk hanya mengganti tanda kurung dengan parens.

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

Kode:

/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

Tidak dikumpulkan dan berkomentar:

%!
%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
luser droog
sumber
@ Thomas Saya bertanya-tanya apakah ini berfungsi untuk mengundang Anda berkomentar. (?)
luser droog
Saya telah memposting implementasi yang lebih lengkap dari ide yang sama di comp.lang.postscript .
luser droog