Operacje na wektorach, odwrotna notacja polska - stworzenie ogólnego zarysu programu.

0

Mam dość duży problem. Dostaliśmy na studiach zadanie, dzięki któremu możemy podwyższyć sobie trochę ocenę z C/C++, z części C dostaliśmy zadanie, ale mam z nim pewien problem. Oto treść zadania:

Poprawka do C

    Program ma umożliwiać wykonywanie operacji na wektorach wczytywanych z pliku. Poniżej lista wymagań:

Możliwe elementy:

    skalar, zapisywany w postaci

S nazwa wartość

    wektor, zapisywany w postaci

V nazwa wymiar_x
wartość_1 ... wartość_n      // dane mogą być podzielone na kilka linijek

    macierz

M nazwa wymiar_nx wymiar_ny
wartość_1 ... wartość_nx*ny  // dane mogą być podzielone na kilka linijek

S, V, M są słowami kluczowymi, nie mogą się zmieniać. nazwa jest dowolnym łańcuchem znakowym, wymiar jest typu int, wartość typu float


Oraz operacje:

+ dodawanie
- odejmowanie
* mnożenie
/ dzielenie
T transponowanie
. iloczyn skalarny
x iloczyn wektorowy
D wyznacznik
R macierz odwrotna


Plik wejściowy korzysta z odwrotnej notacji polskiej, np dla działania (a+b)*c elementy będą w pliku w następującej kolejności:

a   // element S, V lub M
b   // element S, V lub M
+
c   // element S, V lub M
*

Dla działania a*(b+c)

a
b
c
+
*

Program ma zwracać wynik w postaci elementu S, V, lub M na stdout oraz do pliku wyjściowego.

Wszystkie błędy muszą być zwracane na stderr, w przypadku wystąpienia błędu program ma zwrócić odpowienii kod błędu:

0 - wszystko OK, wynik na ekranie
1 - błąd parsowania pliku, w przypadku gdy plik nie spełnia założonego formatowania
2 - błąd działania, w przypadku gdy nie da się wykonać działania, np S T, S V +, niezgodne wymiary macierzy, etc...

Założenia:

    plik wejściowy nie będzia miał wiecej niż 100 argumentów (ale trzeba to sprawdzić!)
    nazwa każdego elementu nie będzie dłuższa niż 16 znaków (ale trzeba to sprawdzić!)


Wymagania:


    a) program musi używać struktur do przechowywania danych S, V, M,
    b) struktury powinna przechowywać pełną informacje o typie i własnościach
    wszystkie działania powinny odbywać się przez funkcje, które przyjmują struktury jako argumenty
    struktury będące wynikiem działania programu powinny mieć element nazwa w postaci index_%03d gdzie zakodowana liczba jest kolejnym numerem operacji, startując od 0
    program musi się składać z trzech jednostek kompilacji:
    a) zaliczenie_c.c - główny kod, zawiera funkcję main()
    b) operacje_m.c - wszystkie operacje matematyczne w postaci funkcji
    c) operacje_io.c - wszystkie operacje wejścia/wyjścia, czyli czytanie/pisanie plików, wyświetlanie wyniku na ekranie
    jednostki kompilacja 4b) i 4c) muszą posiadać odpowiednie pliki nagłówkowe
    programu musi posiadać plik Makefile kompilujący program do pliku 'zaliczenie_c', każdy plik *.c kompilowany osobno, wymagane targets
    a) all - buduj wszystko
    b) clean - wyczyść pliki powstałe w wyniku kompilacji
    wywołanie programu:
    zaliczenie_c plik_wejściowy plik_wyjściowy


Wskazówki:

    do przechowywania kolejki argumentów można wykorzystać tablicę uni, unia może przechowywać każdy z typów danych
    tablica powinna zachowywać się jak stos - dorzucamy elementy na końcu lub usuwamy z końca
    użyć zewnętrznego indeksu do oznaczania aktualnej pozycje na stosie
    podobnie robimy dla tablicy operacji
    dopuszczam możliwość konsultacji ale pewne rodzaje podpowiedzi które będą polegały na podaniu konkretnych rozwiązać związane będą z obniżką oceny o 0.5 stopnia.

Warunki zaliczenia:

    warunkiem uzyskania oceny pozytywnej jest przesłanie działającego programu
    za działający program jest maksymalna ocena 5.0
    za niechlujnie napisany program (brak porządnego formatowania i wcięć będą odejmowane 0.5 stopnia)
    błędy i ostrzeżenia podczas kompilacji skutkują brakiem oceny
    j.w. wspomniano, można konsultować program ale może się to wiązać z obniżeniem oceny, o czy ostaniecie poinformowani przed uzyskaniem odpowiedzi
    program będzie testowany na plikach testowych, wgląd do nich będzie po ocenieniu zadania
    zastrzegam możliwość weryfikacji samodzielności wykonania zadania poprzez rozmowę ze studentem w celu dyskusji zastosowanych rozwiązań (standardowa obrona zadania)

Rozwiązanie proszę przesłać jako archiwum tar.gz.

Ja już pisałem program z operacjami na macierzach wektorach itd. Robiłem osobne funkcje do dodawania, odejmowania, mnożenia i dzielenia. Tylko, że ja to w inny sposób robiłem, po prostu czytałem z pliku i zapisywałem te rzeczy do zmiennych/tablic i później dawałem to jako argumenty do funkcji i jakoś szło. Tutaj jest ta odwrotna notacja Polska i nie wiem jak się za to zabrać, nawet nie wiem jak zacząć, bo zastanawiam się nad dwoma rzeczami.

  1. Zastanawia mnie ten fragment
Plik wejściowy korzysta z odwrotnej notacji polskiej, np dla działania (a+b)*c elementy będą w pliku w następującej kolejności:

a   // element S, V lub M
b   // element S, V lub M
+
c   // element S, V lub M
*

Czyli jak robię np. mnożenie wektora przez skalar załóżmy. Mam wektor [3, 5] i skalar 6.
To co mam to tak zapisać w pliku wejściowym czy co?

3
6
*
5
6
*
+

????
I tak z każdym działaniem?
Po co są te słowa kluczowe, czy może poprzez słowa kluczowe mam to zapisywać typu
S
V
*
I co wtedy zrobić? Mam zrobić jakiś case, że jeśli w tablicy (do której wartości wsadziłem poprzez czytanie z pliku) [n-2] to S, a [n-1] to V i [n] to *, to wtedy wywołuje funkcję mnozenie_skalar_wektor()???

Macie pomysł może od czego zacząć robić ten program, żebym go zrozumiał? Chyba zacznę od czytania po prostu macierzy/wektora/skalaru i przypisaniu ich do odpowiednich struktur? Taki zrobiłem mniej więcej plik wejściowy.

S skalar1 3
S skalar2 5
V wektor1 3
2 4 7
V wektor2 3
3 6 1
macierz_A 3 3
1 2 3
4 5 6
7 8 9
macierz_B 3 3
9 8 7
6 5 4
3 2 1
1
Morgotheron napisał(a):

Czyli jak robię np. mnożenie wektora przez skalar załóżmy. Mam wektor [3, 5] i skalar 6.
To co mam to tak zapisać w pliku wejściowym czy co?

3
6
*
5
6
*
+

według mnie to nie będzie w ten sposób wyglądać. Operacje będą się odbywać na wcześniej zadeklarowanych zmiennych, np:

V vec 2
3 5
S skalar
6

vec
skalar
*

tego typu program powinien zwrócić V 18 30. Natomiast poniższe dane powinny zwrócić błąd bo macierzy o takich wymiarach nie da się pomnożyć:

M m1 2 2
1 2
3 4
M m2 3 3
1 2 3
4 5 6
7 8 9
m1
m2
*
1

No i co co to za problem?

Znasz /masz operacje na tych obiektach, więc robisz to i tyle...

zespolonych, kwaternionów i oktanów brakuje.

0

Ok, napisałem część - wczytywanie, wypisywanie. Zaczynam operacje
To tak, tutaj mam plik z danymi

S skalar1 3
S skalar2 5
V wektor1 3
2 4 7
V wektor2 3
3 6 1
M macierz_A 3 3
1 2 3
4 5 6
7 8 9
M macierz_B 3 3
9 8 7
6 5 4
3 2 1


//Jeszcze nie dawałem do pliku tekstowego żadnych działań, bo mam odnośnie tego pytanie

Plik główny z funkcją main
main.c

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include "operacje_io.h"
#define MAX_NAME 20
#define MAX_ARRAY_DIM 50


typedef struct Vector {
    char keyWordV;
    char name[MAX_NAME];
    int dim;
    double data[MAX_ARRAY_DIM];
} Vector;

typedef struct Scalar {
    char keyWordS;
    char name[MAX_NAME];
    double data;
} Scalar;

typedef struct Matrix {
    char keyWordM;
    char name[MAX_NAME];
    int dimX;
    int dimY;
    double data[MAX_ARRAY_DIM][MAX_ARRAY_DIM];
} Matrix;

typedef struct Element {
    char type;
    union {
        Scalar s;
        Vector v;
        Matrix m;
    } elem;
} Element;

int main (int argc, char *argv[])
{    
    FILE *wz, *wc;  

    if (argc != 3) {                                
    printf("Wrong arguments number\n");
    printf("I should run this way:\n");
    printf("%s source\n",argv[0]);
    exit(1);
    }
    
    if( (wz= fopen(argv[1],"r")) == NULL) {
        printf("Open error %s\n", argv[1]);
        exit(1);
    }
    if( (wc= fopen(argv[2], "w")) == NULL) {
    printf("Open error %s\n", argv[2]);
    exit(2);
    }


    Element elements[10];
    for (int i = 0; i < 6; i++) {
        readElement(wz, &elements[i]);
    }
    fclose(wz);
    fclose(wz);
    
    return 0;

}

Plik z operacjami wejścia/wyjścia. operacje_io.c

#include <stdio.h>
#include <stdlib.h>
#define MAX_NAME 20
#define MAX_ARRAY_DIM 50

typedef struct Vector {
    char keyWordV;
    char name[MAX_NAME];
    int dim;
    double data[MAX_ARRAY_DIM];
} Vector;

typedef struct Scalar {
    char keyWordS;
    char name[MAX_NAME];
    double data;
} Scalar;

typedef struct Matrix {
    char keyWordM;
    char name[MAX_NAME];
    int dimX;
    int dimY;
    double data[MAX_ARRAY_DIM][MAX_ARRAY_DIM];
} Matrix;

typedef struct Element {
    char type;
    union {
        Scalar s;
        Vector v;
        Matrix m;
    } elem;
} Element;

void readScalar(FILE *file, Scalar* s) {
    fscanf(file, " %s ", &s->name[0]);
    fscanf(file, "%10lf", &s->data);
    }
    
void writeScalar(FILE *file, Scalar* s) {
    fprintf(file, "S");
    fprintf(file, " %s \n", s->name);
    fprintf(file, "%10.2lf \n", s->data);
    }

void showScalar(Scalar* s) {
    printf("S");
    printf(" %s \n", s->name);
    printf("%10.2lf \n", s->data);
    }
    
void readVector(FILE *file, Vector* v) {
    fscanf(file, " %s %d ", &v->name[0], &v->dim);
    for (int j = 0; j < v->dim; ++j) {
        fscanf(file, "%10lf", &v->data[j]);
    }
}

void writeVector(FILE *file, Vector* v) {
    fprintf(file, "V");
    fprintf(file, " %s %d \n", v->name, v->dim);
    for (int j = 0; j < v->dim; ++j) {
        fprintf(file, "%10.2lf ", v->data[j]);
    }
    fprintf(file, "\n");
}

void showVector(Vector* v) {
    printf("V");
    printf(" %s %d \n", v->name, v->dim);
    for (int j = 0; j < v->dim; ++j) {
        printf("%10.2lf ", v->data[j]);
    }
    printf("\n");
}

void readMatrix(FILE *file, Matrix* m) {
    fscanf(file, " %s %d %d", &m->name[0], &m->dimX, &m->dimY);
    for (int i = 0; i < m->dimX; ++i)
        for (int j = 0; j < m->dimY; ++j)
            fscanf(file, "%10lf", &m->data[i][j]);
}

void writeMatrix(FILE *file, Matrix* m) {
    fprintf(file, "M");
    fprintf(file, " %s %d %d \n", m->name, m->dimX, m->dimY);
    for (int i = 0; i < m->dimX; ++i) 
    {
        for (int j = 0; j < m->dimY; ++j)
        {
            fprintf(file, "%10.2lf", m->data[i][j]);
        }
        fprintf(file, "\n");
    }
}

void showMatrix(Matrix* m) {
    printf("M");
    printf(" %s %d %d \n", m->name, m->dimX, m->dimY);
    for (int i = 0; i < m->dimX; ++i)
    {
        for (int j = 0; j < m->dimY; ++j)
        {
            printf("%10.2lf", m->data[i][j]);
        }
        printf("\n");
    }
}
void readElement(FILE *file, Element* e) {
    char type;
    fscanf(file, " %c ", &type);
    switch (type) {
    case 'S':
        e->type = 'S';
        readScalar(file, &e->elem.s);
        writeScalar(file, &e->elem.s);
        showScalar(&e->elem.s);
        break;
    case 'V':
        e->type = 'V';
        readVector(file, &e->elem.v);
        writeVector(file, &e->elem.v);
        showVector(&e->elem.v);
        break;
    case 'M':
        e->type = 'M';
        readMatrix(file, &e->elem.m);
        writeMatrix(file, &e->elem.m);
        showMatrix(&e->elem.m);
        break;
    default:
        fputs("Error: unknown token!\n", stderr);
        exit(1);
    }
}

void policz(FILE *file, Element* e1, Element* e2, Element* e3)
{
    char type1, type2, type3;
    fscanf(file, " %c ", &type1);
    fscanf(file, " %c ", &type2);
    fscanf(file, " %c ", &type3);
    
}
int isOperator(char ch){
   if(ch == '+'|| ch == '-'|| ch == '*'|| ch == '/' || ch == '^')
      return 1;//character is an operator
   return -1;//not an operator
}
int isOperand(char ch){
   if(ch == 'S' || ch == 'V' || ch == 'M')
      return 1;//character is an operand
   return -1;//not an operand
}
#define MAX_D 256
double stack[MAX_D];
int depth;



//////Od tego miejsca na razie robię przymiarki jak to pójdzie, więc to jest kod roboczy poniżej

void die(const char *msg)
{
	fprintf(stderr, "%s", msg);
	abort();
}
void push(double v)
{
	if (depth >= MAX_D) die("stack overflow\n");
	stack[depth++] = v;
}
 
double pop()
{
	if (!depth) die("stack underflow\n");
	return stack[--depth];
}
float operation(int a, int b, char op){ //bede dopiero pisal funkcje
   //Perform operation
   if(op == '+')
      return 1;
   else if(op == '-')
      return 1;
   else if(op == '*')
      return 1;
   else if(op == '/')
      return 1;
   else
      return 1; //return negative infinity
}

operacje_io.h

#ifndef WEKTORY
#define WEKTORY

typedef struct Scalar Scalar;
typedef struct Vector Vector;
typedef struct Matrix Matrix;
typedef struct Element Element;

void readScalar(FILE *file, Scalar* s);
void writeScalar(FILE *file, Scalar* s);
void showScalar(Scalar* s);
void readVector(FILE *file, Vector* v);
void writeVector(FILE *file, Vector* v);
void showVector(Vector* v);
void readMatrix(FILE *file, Matrix* m);
void writeMatrix(FILE *file, Matrix* m);
void showMatrix(Matrix* m);
void readElement(FILE *file, Element* e);
void die(const char *msg);
void push(double v);
double pop();
float operation(int a, int b, char op);

#endif

Moje pierwsze pytanie.

  1. Jeśli działania będę zapisywał w ten sposób
macierz1
macierz2
+

To w jaki sposób zrobić tak, żeby on słowo macierz1 i macierz2 skojarzył z char 'M' i po pierwsze wykonał funkcję dla dodawania macierzy (którą zrobię potem (dodaj.macierz(macierz1, macierz2))) Jak zrobić tak, żeby on po nazwie elementu odnalazł w tablicy elementów odpowiedni element (macierz, skalar lub wektor) i go użył (chodzi o tą tablicę elementów, którą używałem, gdy wczytywałem obiekty).

  1. Problemem jest jeszcze to, że jak zrobię normalnego switch-case jak zrobiłem przy dodawaniu to jeszcze ważne jest to, żebym wybrał poprawną funkcję dodawania, bo mogą być różne opcje. Załóżmy takie mnożenie. Może być mnożenie dwóch macierzy, mnożenie dwóch wektorów, mnożenie macierzy przez skalar. Więc chyba najrozsądniej będzie zrobić switch case dla działania +,-,*,/, a potem zrobić ifa i w zależności od tego czy dwa poprzednie elementy były np. dwoma skalarami to wywołać funkcję np. dodajSkalary(skalar1, skalar2);

  2. Nic mi nie wypisuje i nie wiem dlaczego, za to wyświetlanie na ekranie działa bardzo dobrze.

0

Moje pierwsze pytanie.

  1. Jeśli działania będę zapisywał w ten sposób
macierz1
macierz2
+

To w jaki sposób zrobić tak, żeby on słowo macierz1 i macierz2 skojarzył z char 'M' i po pierwsze wykonał funkcję dla dodawania macierzy (którą zrobię potem (dodaj.macierz(macierz1, macierz2))) Jak zrobić tak, żeby on po nazwie elementu odnalazł w tablicy elementów odpowiedni element (macierz, skalar lub wektor) i go użył (chodzi o tą tablicę elementów, którą używałem, gdy wczytywałem obiekty).

przecież masz nazwy tych argumentów, więc chyba znajszesz.

  1. Problemem jest jeszcze to, że jak zrobię normalnego switch-case jak zrobiłem przy dodawaniu to jeszcze ważne jest to, żebym wybrał poprawną funkcję dodawania, bo mogą być różne opcje. Załóżmy takie mnożenie. Może być mnożenie dwóch macierzy, mnożenie dwóch wektorów, mnożenie macierzy przez skalar. Więc chyba najrozsądniej będzie zrobić switch case dla działania +,-,*,/, a potem zrobić ifa i w zależności od tego czy dwa poprzednie elementy były np. dwoma skalarami to wywołać funkcję np. dodajSkalary(skalar1, skalar2);

int add(Result, A, B) // param. przez referencje
{
if ( A niezgodne z B ) return -1; // albo jakiś tam tekst/kod błędu

// dalej obliczamy...
Result = A + B.

result 0; // ok, albo i nie, np.: 7/0 = błąd, albo próba odwracania macierzy osobliwej...
}

1 użytkowników online, w tym zalogowanych: 0, gości: 1