zadanie ku skuske
This commit is contained in:
parent
19f7061562
commit
6b88707e67
16
sk2/Makefile
Normal file
16
sk2/Makefile
Normal file
@ -0,0 +1,16 @@
|
||||
CC = gcc
|
||||
CFLAGS = -Wall -Wextra -std=c11 -O2
|
||||
|
||||
all: calculator
|
||||
|
||||
calculator: main.o calculator.o
|
||||
$(CC) $(CFLAGS) -o calculator main.o calculator.o -lm
|
||||
|
||||
main.o: main.c calculator.h
|
||||
$(CC) $(CFLAGS) -c main.c
|
||||
|
||||
calculator.o: calculator.c calculator.h
|
||||
$(CC) $(CFLAGS) -c calculator.c
|
||||
|
||||
clean:
|
||||
rm -f *.o calculator
|
||||
39
sk2/Readme.md
Normal file
39
sk2/Readme.md
Normal file
@ -0,0 +1,39 @@
|
||||
# Vedecká kalkulačka
|
||||
|
||||
## Zadanie
|
||||
Napísať program v jazyku C, ktorý vyhodnotí matematický výraz v infixnej notácii zadaný zo stdin.
|
||||
Podporované operácie: +, -, *, /, zátvorky (), sin(), cos(), sqrt(), log(), ^2 (druhá mocnina).
|
||||
Presnosť: minimálne 2 desatinné miesta (použité double).
|
||||
|
||||
## Stručný opis funkčnosti
|
||||
Program číta riadky zo stdin, vyhodnotí výraz pomocou funkcie `evaluate()` a vypíše výsledok s 2 desatinnými miestami.
|
||||
Príklad použitia:
|
||||
make all
|
||||
./calculator
|
||||
(2 + 3) * 2
|
||||
10.00
|
||||
sin(3.1416 / 2)
|
||||
1.00
|
||||
|
||||
|
||||
## Stručný opis riešenia
|
||||
- Použitý Shunting-yard algoritmus na konverziu infix na postfix.
|
||||
- Tokenizácia výrazu na čísla, operátory, funkcie, zátvorky.
|
||||
- Vyhodnotenie postfixu pomocou zásobníka.
|
||||
- Ošetrenie chýb: delenie nulou, negatívna odmocnina, log <=0, nesprávne zátvorky.
|
||||
- Unary operátory/funkcie spracované v postfix vyhodnotení.
|
||||
|
||||
## Podmienky, za ktorých funguje správne
|
||||
- Výraz môže obsahovať medzery (ignorované).
|
||||
- Funkcie vyžadujú zátvorky: sin(x), nie sin x.
|
||||
- Druhá mocnina ako ^2 (postfix unary).
|
||||
- Čísla: desatinné, negatívne.
|
||||
- Nefunguje pre pokročilé veci ako exponentiácia (^ namiesto ^2), viacargumentové funkcie.
|
||||
|
||||
## Zoznam použitých zdrojov
|
||||
- Algoritmus Shunting-yard: Wikipedia[](https://en.wikipedia.org/wiki/Shunting_yard_algorithm)
|
||||
- Dokumentácia <math.h>: man pages (sin, cos, sqrt, log)
|
||||
- Základný tokenizer a zásobník: vlastná implementácia inšpirovaná prednáškami a DU.
|
||||
- Šablóna Makefile: štandardná pre C projekty.
|
||||
|
||||
Autor: Nataliia Kobryn
|
||||
197
sk2/calculator.c
Normal file
197
sk2/calculator.c
Normal file
@ -0,0 +1,197 @@
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include "calculator.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define MAX_TOKEN_LEN 256
|
||||
|
||||
typedef enum{
|
||||
TOKEN_NUMBER,
|
||||
TOKEN_FUNCTION,
|
||||
TOKEN_OPERATOR,
|
||||
TOKEN_LPAREN,
|
||||
TOKEN_RPAREN,
|
||||
TOKEN_UNKNOWN
|
||||
} TokenType;
|
||||
|
||||
|
||||
|
||||
typedef struct{
|
||||
TokenType type;
|
||||
char value[MAX_TOKEN_LEN];
|
||||
} Token;
|
||||
|
||||
static int get_precedence(const char *op){
|
||||
if (strcmp(op,"+")==0||strcmp(op,"-")==0) return 1;
|
||||
if (strcmp(op,"*")==0||strcmp(op,"/")==0) return 2;
|
||||
if (strcmp(op,"^2")==0) return 3;
|
||||
if (strcmp(op,"sin")==0||strcmp(op,"cos")==0||strcmp(op,"sqrt")==0||strcmp(op,"log")==0) return 4;
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
static bool is_right_associative(const char *op){
|
||||
return strcmp(op,"^2")==0;
|
||||
}
|
||||
|
||||
static int tokenize(const char *expr, Token *tokens, int max_tokens){
|
||||
int count= 0;
|
||||
const char *p = expr;
|
||||
while(*p){
|
||||
if(isspace(*p)){p++; continue;}
|
||||
|
||||
if(isdigit(*p)||*p=='.'||(*p=='-'&&(isdigit(p[1])||p[1]=='.'))){
|
||||
char *end;
|
||||
strtod(p,&end);
|
||||
strncpy(tokens[count].value, p ,end-p);
|
||||
tokens[count].value[end-p] = '\0';
|
||||
tokens[count].type = TOKEN_NUMBER;
|
||||
p = end;
|
||||
// p++;
|
||||
}
|
||||
|
||||
else if(*p=='('){
|
||||
strcpy(tokens[count].value, "(");
|
||||
tokens[count].type = TOKEN_LPAREN;
|
||||
p++;
|
||||
}
|
||||
else if(*p==')'){
|
||||
strcpy(tokens[count].value, ")");
|
||||
tokens[count].type = TOKEN_RPAREN;
|
||||
p++;
|
||||
}
|
||||
else if(isalpha(*p)){
|
||||
char func[5];
|
||||
int i =0;
|
||||
while(isalpha(*p)&&i<4){func[i++]=*p++;};
|
||||
func[i] = '\0';
|
||||
if (strcmp(func,"cos")==0||strcmp(func,"sin")==0||strcmp(func,"sqrt")==0||strcmp(func,"log")==0){
|
||||
strcpy(tokens[count].value, func);
|
||||
tokens[count].type = TOKEN_FUNCTION;
|
||||
}
|
||||
else{ return -1;}
|
||||
}
|
||||
else{
|
||||
char op[3];
|
||||
op[0] = *p;
|
||||
op[1] = '\0';
|
||||
if (*p == '^' &&p[1]=='2'){
|
||||
strcpy(op,"^2");
|
||||
p+=2;
|
||||
}
|
||||
p++;
|
||||
strcpy(tokens[count].value, op);
|
||||
tokens[count].type = TOKEN_OPERATOR;
|
||||
}
|
||||
count++;
|
||||
if (count>=max_tokens) return -1;
|
||||
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static int infix_to_postfix(Token *infix, int infix_count, Token *postfix){
|
||||
Token op_stack[MAX_TOKEN_LEN];
|
||||
int on_top = -1;
|
||||
int postfix_count = 0;
|
||||
for (int i = 0; i<infix_count;i++){
|
||||
Token t = infix[i];
|
||||
switch(t.type){
|
||||
case TOKEN_NUMBER:
|
||||
postfix[postfix_count++] = t;
|
||||
break;
|
||||
|
||||
case TOKEN_FUNCTION:
|
||||
op_stack[++on_top] = t;
|
||||
break;
|
||||
case TOKEN_OPERATOR:
|
||||
while(on_top>=0){
|
||||
Token top = op_stack[on_top];
|
||||
if(top.type!=TOKEN_OPERATOR&&top.type!=TOKEN_FUNCTION) break;
|
||||
int prec_t = get_precedence(t.value);
|
||||
int prec_top = get_precedence(top.value);
|
||||
if(prec_top<prec_t||(prec_t==prec_top&&!is_right_associative(t.value))) break;
|
||||
postfix[postfix_count++] = op_stack[on_top--];
|
||||
}
|
||||
op_stack[++on_top] = t;
|
||||
break;
|
||||
case TOKEN_LPAREN:
|
||||
op_stack[++on_top] = t;
|
||||
break;
|
||||
case TOKEN_RPAREN:
|
||||
while (on_top>=0&&strcmp(op_stack[on_top].value,"(")!=0){
|
||||
postfix[postfix_count++] = op_stack[on_top--];
|
||||
}
|
||||
if (on_top<0) return -1;
|
||||
on_top--;
|
||||
break;
|
||||
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
while(on_top>=0){
|
||||
if(strcmp(op_stack[on_top].value,"(")==0) return -1;
|
||||
postfix[postfix_count++] = op_stack[on_top--];
|
||||
}
|
||||
return postfix_count;
|
||||
}
|
||||
|
||||
static double evaluate_postfix(Token *postfix, int count){
|
||||
double stack[MAX_TOKEN_LEN];
|
||||
int top= -1;
|
||||
|
||||
for (int i = 0; i<count; i++){
|
||||
Token t =postfix[i];
|
||||
if (t.type == TOKEN_NUMBER){
|
||||
stack[++top] = atof(t.value);
|
||||
}
|
||||
else{
|
||||
if (t.type == TOKEN_FUNCTION|| (strcmp(t.value,"^2")==0)){
|
||||
if (top<0) return NAN;
|
||||
double a= stack[top--];
|
||||
if(strcmp(t.value, "cos")==0) stack[++top] = cos(a);
|
||||
else if(strcmp(t.value, "sin")==0) stack[++top] = sin(a);
|
||||
else if(strcmp(t.value, "sqrt")==0){
|
||||
if (a<0) return NAN;
|
||||
stack[++top] = sqrt(a);
|
||||
}
|
||||
else if(strcmp(t.value, "log")==0){
|
||||
if (a<=0) return NAN;
|
||||
stack[++top] = log(a);
|
||||
}
|
||||
else if(strcmp(t.value,"^2")==0) stack[++top] = a*a;
|
||||
else return NAN;
|
||||
}
|
||||
else{
|
||||
if (top<1) return NAN;
|
||||
double b = stack[top--];
|
||||
double a = stack[top--];
|
||||
if (strcmp(t.value,"+")==0) stack[++top] = a+b;
|
||||
else if (strcmp(t.value,"-")==0) stack[++top] = a-b;
|
||||
else if (strcmp(t.value,"*")==0) stack[++top] = a*b;
|
||||
else if (strcmp(t.value,"/")==0){
|
||||
if (b==0) return NAN;
|
||||
stack[++top] = a/b;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (top!=0) return NAN;
|
||||
return stack[0];
|
||||
}
|
||||
|
||||
double evaluate(const char *expression){
|
||||
Token infix[MAX_TOKEN_LEN];
|
||||
int infix_count = tokenize(expression, infix,MAX_TOKEN_LEN);
|
||||
if (infix_count<0) return NAN;
|
||||
|
||||
Token postfix[MAX_TOKEN_LEN];
|
||||
int postfix_count = infix_to_postfix(infix,infix_count,postfix);
|
||||
if (postfix_count<0) return NAN;
|
||||
|
||||
return evaluate_postfix(postfix, postfix_count);
|
||||
}
|
||||
18
sk2/calculator.h
Normal file
18
sk2/calculator.h
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef CALCULATOR_H
|
||||
#defien CALCULATOR_H
|
||||
|
||||
|
||||
/**
|
||||
* Vyhodnotí matematický výraz v infixnej notácii.
|
||||
* Podporované operátory: + - * / ^2 (druhá mocnina ako postfix unary)
|
||||
* Podporované funkcie: sin(), cos(), sqrt(), log() (prirodzený logaritmus)
|
||||
* Podporuje zátvorky () a desatinné čísla s presnosťou double.
|
||||
*
|
||||
* Príklady: "(2 + 3) * 2", "sin(3.14)", "sqrt(16) + log(2.718)"
|
||||
*
|
||||
* @param expression reťazec s výrazom
|
||||
* @return výsledok ako double, alebo NAN pri chybe
|
||||
*/
|
||||
double evaluate(const char *expression);
|
||||
|
||||
#endif
|
||||
20
sk2/main.c
Normal file
20
sk2/main.c
Normal file
@ -0,0 +1,20 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "calculator.h"
|
||||
#include <math.h>
|
||||
|
||||
int main(){
|
||||
char line[1024];
|
||||
while(fgets(line,sizeof(line),stdin)){
|
||||
line[strcspn(line, "\n")] = '\0';
|
||||
if (strlen(line) == 0) continue;
|
||||
|
||||
double result = evaluate(line);
|
||||
if (isnan(result)) printf("Chyba pri vypocitani vyrazu\n");
|
||||
else{
|
||||
printf("%.2f\n", result);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user