Eu pot sa ajut doar cu o implementare în C/C++. Codul e scris puţin în grabă, dar sper că îl va traduce cineva în Java.
Folosind polimorfismul, am plecat de la ideea de
expresie. Am considerat că atât operaţiile aritmetice cât şi funcţiile sunt expresii, pe care le-am clasificat în
expresii unare,
expresii binare,
valori constante,
variabile şi
funcţii (cu un argument sau cu mai multe argumente).
Fiecare expresie poate fi
formatată,
evaluata (in functie de x),
derivata, dar majoritatea acestor operaţii trebuie implementate în clasele "moştenitoare".
Clasele
String şi
Array nu există; le-am folosit doar în mod simbolic; înlocuiţi-le cu un echivalent.
La evaluarea expresiilor am folosit tipul
double; dacă este nevoie, înlocuiţi-l cu alt tip.
- /* Expression
-
- Clasa de baza pentru orice tip de expresie/functie/valoare
- */
-
- class Expression{
- public:
- virtual int getPriority()const //prrioritatea operatorilor; folosita doar la afisare
- {
- return 1; //1=prioritatea adunarii
- };
-
- virtual String formatExpression(int parentPriority=1)const=0;
- virtual double evaluate(double x)const=0; //x = parametrul variabil al functiei
- virtual Expression*derrive()const=0;
- };
-
- /* UnaryExpression
-
- Clasa de baza pentru operatorii aritmetici unari, precum negarea unei valori (-x)
- */
- class UnaryExpression:public Expression{
- Expression *operand;
- public:
- inline UnaryExpression(Expression* op):operand(op){}
- virtual ~UnaryExpression(){if(operand)delete operand;}
- };
-
- /* BinaryExpression
-
- Clasa de baza pentru operatorii aritmetici binari, precum:
- adunare, scadere, inmultire, impartire, ridicare la putere, etc.
- */
- class BinaryExpression:public Expression{
- Expression *leftOperand,*rightOperand;
- public:
- inline BinaryExpression(Expression *left,Expression *right)
- :leftOperand(left),rightOperand(right){}
-
- virtual ~BinaryExpression()
- {
- if(leftOperand)delete leftOperand;
- if(rightOperand)delete rightOperand;
- }
- };
-
- /* Function - poate fi vazuta ca o expresie cu orice numar de operanzi*/
-
- class Function:public Expression{
- Array<Expression*> arguments; //o lista de parametri ai functiei (0,1, sau mai multi)
- public:
- virtual int getPriority()const{return 3;};//prioritatea functiilor
- virtual String getFunctionName()const=0;
- String formatExpression(int parentPriority)const
- {
- //incercam o implementare "default"
- String str="";
-
- if(parentPriority>this->getPriority())
- str+= "(";
-
- str+= getFunctionName() + "(";
-
- str+= arguments[0].formatExpression();
-
- for(int i=1;i<arguments.length;i++)
- str+= "," arguments[i].formatExpression();
- //prioritatea parametrului nu este importanta; este deja separat prin paranteze
- str+= ")";
-
- if(parentPriority>this->getPriority())
- str+= "(";
-
- return str;
- };
-
- virtual ~Function(){ /* Distruge argumentele alocate sub forma de expresii */}
- };
-
- /* Functie cu un singur parametru - model frecvent intalnit */
- class UnaryFunction:public Function{
- public:
- inline UnaryFunction(Expression*exp){arguments.push(exp);}
- };
-
- /* Value - o valoare cunoscuta */
-
- class Value:public Expression{
- double value;
- public:
- inline Value(double val):value(val){}
-
- String formatExpression(int parentPriority)const{return (String)value;} //conversia la String
- double evaluate(double x){return value;} //independent de argument
- Expression*derrive(){return Value(0);} //independent de valoare
- };
-
- /* Variable - o necunoscuta (parametrul functiei)*/
-
- class Variable:public Expression{
- String variable_name;
- public:
- Variable():variable_name("x"){} //alegem "x" ca nume implicit al variabilei
-
- String formatExpression(int parentPriority)const{return variable_name;}
- double evaluate(double x)const{return x;} //x = parametrul functiei
- Expression*derrive()const{return Value(1);} //independent de valoarea variabilei
- };
Următoarele clase sunt polimorfice şi din ele trebuie derivate noile clase:
Expression |---
UnaryExpression |---
BinaryExpression |---
Function |------
UnaryFunctionclasa Expression -- furnizează 4 funcţii polimorfice:
-- int getPriority() -- folosită la formatarea expresiilor; se supraîncarcă pentru a indica propritatea operatorilor
-- String formatExpression([int priority]) -- implementată în clasele descendente, formatează conţinutul expresiilor
-- double evaluate(double) -- implementată în clasele descendente, evaluează expresia în funcţie de variabila x
-- Expression* derrive() -- implementată în clasele descendente, returnează o expresie nouă, prin derivarea expresiei curente
clasa UnaryExpression -- conţine ca variabilă membră, o expresie, pe post de operand unic.
clasa BinaryExpression -- în mod asemănător cu UnaryExpression, conţine 2 expresii, pe post de operanzi. Operaţiile aritmetice de bază pornesc de la această clasă şi implementează funcţiile de formatare, evaluare şi derivare.
clasa Function -- clasa de bază pentru funcţii predefinite. Clasa memorează o listă de argumente şi implementează funcţia de formatare, folosind notaţia tipică a funcţiilor.
Adaugă o nouă funcţie polimorfică:
String getFunctionName(), a cărei rol este de a furniza un nume pentru funcţia de formatare.
Rămân 2 funcţii virtuale neimplementate:
-- double evaluate(double)
-- Expression* derrive()
UnaryFunction -- nu aduce nimic nou; doar simplifică uşor scrierea funcţiilor cu un singur argument
Următoarele clase sunt finalizate:
Value (implementează UnaryExpression) -- memorează valori de tip
double. Funcţiile de evaluare,formatare şi derivare sunt deja implementate. Ori de câte ori avem nevoie să memorăm o valoare într-o expresie, folosim constructorul:
new Value(<valoare>).
Variable (implementează UnaryExpression) -- memorează parametrul x al funcţiei pe care vrem să o evaluăm. Funcţiile de evaluare,formatare şi derivare sunt deja implementate. Când avem nevoie să introducem variabila x într-o expresie, folosim constructorul:
new Variable()
Câteva exemple de implementare pentru clasele de mai sus:
Negation (implementează UnaryExpression) -- este negarea algebrică a unei expresii (de forma
-x). Clasa memorează expresia ce trebuie negată, iar funcţiile de formatare, evaluare, derivare sunt implementate în funcţie de expresia respectivă.
- /* 1. Expresie unara */
-
- class Negation:public UnaryExpression{
- public:
- Negation(Expression*exp):UnaryExpression(exp){}
-
- int getPriority()const{return 2;} //2=prioritatea inmultirii
- double evaluate(double x)const{return -operand.evaluate(x);}
- Expression*derrive()const{return -operand.derrive();}
-
- String formatExpression(int parentPriority)const
- {return "(-" + operand.formatExpression() + ")";};
-
- };
Addition, Subtraction, Product (implementează BinaryExpression) -- cei 2 operanzi sunt memoraţi, iar operaţiile de evaluare, formatare şi derivare se fac în funcţie de aceştia.
- /* 2. Expresii binare */
-
- class Addition:public BinaryExpression{
- public:
- inline Addition(Expression *left,Expression *right)
- :BinaryExpression(left,right){}
-
- double evaluate(double x)const
- {
- return leftOperand.evaluate(x)+rightOperand.evaluate(x);
- }
-
- Expression*derrive()const
- {
- return new Addition(
- leftOperand->derrive(),
- rightOperand->derrive()
- );
- }
- String formatExpression(int parentPriority)const
- {
- if(parentPriority > this->getPriority())
- return "("+leftOperand.formatExpression() + " + " + rightOperand.formatExpression()+")";
- else
- return leftOperand.formatExpression() + " + " + rightOperand.formatExpression();
- };
- };
-
- class Subtraction:public BinaryExpression{
- public:
- inline Subtraction(Expression *left,Expression *right)
- :BinaryExpression(left,right){}
-
- double evaluate(double x)const
- {
- return leftOperant.evaluate(x)-rightOperand.evaluate(x);
- }
-
- Expression*derrive()const
- {
- return new Subtraction(
- leftOperand->derrive(),
- rightOperand->derrive()
- );
- }
- String formatExpression(int parentPriority)const
- {
- if(parentPriority > this->getPriority())
- return "("+leftOperand.formatExpression() + " - " + rightOperand.formatExpression()+")";
- else
- return leftOperand.formatExpression() + " - " + rightOperand.formatExpression();
- };
- };
-
- class Product:public BinaryExpression{
- public:
- inline Addition(Expression *left,Expression *right)
- :BinaryExpression(left,right){}
-
- double evaluate(double x)const
- {
- return leftOperant.evaluate(x)+rightOperand.evaluate(x);
- }
-
- Expression*derrive()const
- {
- return new Subtraction(
- new Product(leftOperand->derrive(),rightOperand),
- new Product(leftOperand,rightOperand->derrive())
- );
- }
- String formatExpression(int parentPriority)const
- {
- if(parentPriority > this->getPriority())
- return "("+leftOperand.formatExpression() + " * " + rightOperand.formatExpression()+")";
- else
- return leftOperand.formatExpression() + " * " + rightOperand.formatExpression();
- };
- };
Sinus, Cosinus (implementează UnaryFunction) -- singurul argument este memorat (aproape) automat în clasa UnaryFunction. Datorită faptului că funcţia de formatare este implementată deja în clasa Function, este suficient ca noi să definim funcţia "getFunctionName".
Funcţia de evaluare apelează funcţia de evaluare a argumentului (argument[0]->evaluate). Rezultatul este folosit ca argument pentru funcţia curentă.
Funcţiile de derivare de mai jos sunt un exemplu de utilizare pentru clasele pe care le-am definit.
- /* 3. Functii cu un singur argument */
-
- class Sinus:public UnaryFunction{
- public:
- Sinus(Expression* argument):UnaryFunction(argument){}
-
- String getFunctionName()const{return "sin";}
- double evaluate(double x)const{return sin(argument[0]->evaluate(x));}
- Expression*derrive()const
- {
- return new Product(
- new Cosinus(argument[0]),
- argument[0]->derrive()
- }
- }
- };
-
- class Cosinus:public UnaryFunction{
- public:
- Sinus(Expression* argument):UnaryFunction(argument){}
-
- String getFunctionName()const{return "cos";}
- double evaluate(double x)const{return cos(argument[0]->evaluate(x));}
- Expression*derrive()const
- {
- return new Product(
- new Negation(new Sinus(argument[0])),
- argument[0]->derrive()
- }
- }
- };
Mai trebuie implementate câteva funcţii şi câteva operaţii matematice, iar de aici calculatorul se descurcă singur.
Sper că ajută. Codul nu e scris să funcţioneze direct; are mai mult rolul de a explica ideea de implementare.