• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Sprawdzanie poprawności wprowadzonych liczb zespolonych

VPS Starter Arubacloud
0 głosów
546 wizyt
pytanie zadane 17 listopada 2015 w C i C++ przez Delebrith Początkujący (250 p.)
Mam zadanie aby napisać program sumujący n liczb zespolonych. Liczby mogą być wprowadzane w postaci kanonicznej (2+0.5j) lub wykładniczej (3.56exp(-2jpi)). Program ma też sprawdzać poprawność wprowadzonych liczb. Uznałam że liczby wprowadzę w postaci zmiennych typu char. Problem pojawił się, gdy pisałam funkcję sprawdzającą poprawność - jeżeli chcę sprawdzić każdy znak i wskazać miejsce błędu w kodzie pojawia się MNÓSTWO instrukcji warunkowych. Czy jest jakiś sposób by uniknąć takiego bałaganu w kodzie źródłowym?

3 odpowiedzi

0 głosów
odpowiedź 17 listopada 2015 przez draghan VIP (106,230 p.)
wybrane 21 listopada 2015 przez Delebrith
 
Najlepsza
Niestety, taki urok C. :)

Nie wiem, jak to wygląda u Ciebie w tej chwili, ale radzę wydzielić jak najwięcej funkcji - im mniejsze, tym łatwiej to ogarnąć. ;)
0 głosów
odpowiedź 17 listopada 2015 przez Krawiec91 Pasjonat (19,600 p.)
Myślę, że lepiej byłoby stworzyć strukturę/klasę dla liczb zespolonych (cz. rzeczywista, cz. urojona jako zmienne np. typu double, w klasie funkcje do wprowadzania tych liczb oraz sprawdzenie poprawności).  Inną możliwością może być też użycie klasy complex.
http://www.cplusplus.com/reference/complex/
Nie wiem na jakim poziomie jesteś, jeśli chodzi o C++, głównie chodzi mi obiektówkę (klasy, etc), ciężko  mi co więcej powiedzieć. Pomysł zrobienia tego zmiennymi char jest kiepski, prosta struktura i funkcje do niej prościej rozwiążą ten problem.
komentarz 17 listopada 2015 przez Delebrith Początkujący (250 p.)
Otóż to. Stąd się wziął mój problem. I gdy chciałam napisać fukcję sprawdzającą poprawność instrukcje warunkowe rozmnażały mi się w zastraszającym tempie, bo sprawdzałam po kolei znaki.
komentarz 18 listopada 2015 przez Krawiec91 Pasjonat (19,600 p.)
No to ostro, ostro... W takim wypadku, to nie widzę innego wyjścia jak powrócić do tablicy char na wejściu. Strukturę bym zostawił, bo tak czy siak się przyda, raz że dodawania liczb, a dwa do konwersji z postaci kanonicznej na wykładniczą i na odwrót. Sprawdzanie znak po znaku to masakra jest, jakby była jedna postać liczby to pół biedy, a tutaj wchodzi w grę rozpoznanie, która postać liczby została wprowadzona, a po tym zdekodowanie tego i wyłapanie ewentualnych błędów. Jeszcze pozostaje kwestia, jak idiotoodporny;) ma być ten program, bo możliwości wywalenia tego programu jest sporo. Kombinowałbym z użyciem funkcji z bibliotek stdlib.h oraz string.h. Do rozróżnienia jaka postać liczby została wprowadzona, użyłbym funkcji strstr() w celu szukania "exp", wyłapywanie liczb ze stringa za pomocą atof(), co do szukania części urojonej - szukać za pomocą strstr() ciągu "-j" i "+j", potem przesunąć wskaźnik na adres litery "j" i w dalszej części stringa próbować znaleźć liczbę. W zależności od wyników tych poszukiwań, stwierdzać czy dane są poprawnie wprowadzone czy nie. Ostra rozkmina muszę przyznać, aż pewnie siądę jutro, bo ostro mi wjechało to zadanie na ambicję.
komentarz 18 listopada 2015 przez draghan VIP (106,230 p.)
edycja 18 listopada 2015 przez draghan

Nie jest to jakies mega skomplikowane zadanie, zwykłe parsowanie tekstu. Największym wrogiem tutaj jest język C, niestety.

Jak już pisałem wyżej - rozbić zadanie na łatwe do ogarnięcia podprogramy. Spójrz na taki szablon:

#include <stdio.h>
#include <string.h>


typedef enum
{
    expotential, canonic
} type;

typedef enum
{
    false = 0, true
} bool;


type check_type(const char s[]) // sprawdza potencjalny typ - należy napisać osobne funkcje do sprawdzenia poprawności każdego z typów
{
    if(strstr(s, "exp") != NULL) return expotential; // jeśli zawiera frazę "exp", to kandydat na postać ekspotencjalną
    else return canonic; // w przeciwyn wypadku wrzucamy do worka CANONIC
}

unsigned get_length_of_number(const char s[], bool float_precision, bool plus_or_minus) // bardzo przydatna funkcja, zliczająca ilość znaków, które nadają się na liczbę
{
    // flaga plus_or_minus mówi o tym, czy zezwalamy na znak poprzedzający
    // flaga float_precision mówi o tym, czy pozwalamy na umieszczenie znaku kropki w liczbie
    unsigned i = 0;
    if(s[0] == '+' || s[0] == '-') // jeśli user umieścił na początku plus lub minus, sprawdzenie zaczynamy od indeksu 1.
    {
        if(!plus_or_minus) return 0; // jeśli nie pozwalamy na poprzedzający liczbę znak...
        i = 1;
    }
    for(; i < strlen(s); ++i)
    {
        if(isdigit(s[i]) || (float_precision == true && s[i] == '.')) continue;
        else break;
    }
    return i;
}

bool validate_canonic(const char s[]) // od razu można dać jako parametr wskaźnik do struktury reprezentującej liczbę zespoloną
{
    // wzorzec jest taki: +/-liczba rzeczywista, znak +/-,j, liczba rzeczywista
    // zakładam dla uproszczenia na sztywno długość każdej tablicy na 100
    char temp[100] = "";
    char a[100] = "";
    char b[100] = "";
    unsigned length = get_length_of_number(s, true, true);

    if(length == 0)
    {
        printf("Najpierw podaj parametr \'a\'!");
        return false;
    }
    strncpy(a, s, length); // i już mamy w tablicy a poprawną liczbę - gotową do konwersji
    strcpy(temp, s+length); // w zmiennej 'temp' zostawiamy string wejściowy z wyciętą pierwszą liczbą

    strcat(temp, "");
    if(temp[0] != '+' && temp[0] != '-')
    {
        printf("Po parametrze \'a\' spodziewano sie znaku +/-!");
        return false;
    }
    if(temp[1] != 'j')
    {
        printf("Po znaku +/- spodziewano sie jednostki urojonej \'j\'!");
        return false;
    }

    length = get_length_of_number(temp+2, true, false);
    if(length == 0)
    {
        printf("Po jednostce urojonej \'j\' spodziewano sie parametru \'b\'!");
        return false;
    }
    strncpy(b, temp+2, length); // i już mamy w tablicy b poprawną liczbę - gotową do konwersji


    if(strlen(temp) != length + 2)
    {
        printf("Uwaga - po poprawnej liczbie podano niezidentyfikowane znaki: \"");
        fputs(temp + 2 + length, stdout);
        printf("\"!");
    }

    // tu może być konwersja na liczby i przypisanie do struktury...
    printf("\na = ");
    fputs(a, stdout);
    printf("\nb = ");
    fputs(b, stdout);
    return true;
}


int main()
{
    char s[] = "-15+j15.3y";

    if(check_type(s) == expotential)
    {
        printf("Byc moze forma wykladnicza...\n");
    }
    else
    {
        printf("Byc moze forma kanoniczna...\n");
        if(validate_canonic(s))
        {
            printf("\nkanoniczna!");
        }
        else
        {
            printf("\nblad!");
        }
    }
    return 0;
}

Potrafi już poprawnie rozpoznać postać kanoniczną. Oczywiście to nie jest doskonałe - np. przydałaby się funkcja, usuwająca z łańcucha wszelkie białe znaki. ;) Postać wykładniczą można wyłuskać kodem o podobnym stopniu "trudności". ;)

A tak nawiasem - na PG też uczą C'89. ;)

EDIT:
"Program powinien akceptować również liczby czysto rzeczywiste oraz czysto urojone" - tego nie doczytałem, ale trudne do wprowadzenia to nie jest. ;)
 

komentarz 18 listopada 2015 przez Delebrith Początkujący (250 p.)
edycja 18 listopada 2015 przez Delebrith
Nie wiem tylko jak mogę tutaj znajdować błąd. Jeżeli użytkownik wpisze coś w rodzaju 13.5+js12 program musi wskazać na s w kolejnym wierszu... Znajdowanie i wskazywanie błędu jest dla mnie największym problemem.
komentarz 18 listopada 2015 przez draghan VIP (106,230 p.)
1. Określasz zgrubnie kategorię tego, co wprowadził user. Inaczej będziesz przetwarzać ciąg z postacią wykładniczą, a inaczej ciąg z postacią algebraiczną.

2. Skoro wiesz z grubsza, którą postać masz znaleźć, trzeba po kolei rozpatrywać to, co jest w łańcuchu i jeśli nie jest to coś, czego oczekujesz, w tym miejscu podajesz komunikat błędu.

Jesteś w tak dobrej sytuacji, że nie musisz poprawiać błędnie wprowadzonej liczby, tylko wypisać co się nie zgadza z założonym wzorcem.

Jeśli potrzebujesz liczbę - sprawdzasz czy jest wprowadzona liczba, jeśli potrzebujesz literkę "j", sprawdzasz czy następna w kolejności jest literka "j"...
I po każdym udanym sprawdzeniu, wycinasz sprawdzony fragment łańcucha, aż nie zostanie Ci nic do sprawdzenia. Wycięta część jest już przetworzona i niepotrzebna, więc ją wyrzucamy lub ewentualnie przypisujemy do odpowiedniego bufora - jeśli interesuje nas wartość. To naprawdę nie jest trudne. ;) Najlepiej rozrysować to sobie najpierw na kartce, bo programy w C są mało przejrzyste dla niedoświadczonych programistów.

Wieczorem, kiedy wrócę z zajęć, możemy nad tym trochę popracować. :)
0 głosów
odpowiedź 24 listopada 2015 przez Delebrith Początkujący (250 p.)

Powracam z pytaniem o to samo zadanie, jednak o inną rzecz. Na podstawie kodu podanego przed @draghan próbowałam napisać funkcję sprawdzającą postać wykładniczą. Częściowo się to udało. Jednak pojawia się pewien błąd, który nie wiem z czego może wynikać.



Mój kod...

 

#include<stdio.h>
#include<string.h>
#include<ctype.h>

typedef enum
{
    wykladnicza, kanoniczna
} type;

typedef enum
{
    false = 0, true
} bool;

type postac(const char s[]) /* sprawdza potencjalny typ - nale¿y napisaæ osobne funkcje do sprawdzenia poprawnoœci ka¿dego z typów*/
{
    if(strstr(s, "exp") != NULL) return wykladnicza; /* jeœli zawiera frazê "exp", to kandydat na postaæ wykladnicza*/
    else return kanoniczna; /* w przeciwyn wypadku rozpatrujemy kanonniczna*/
}

int get_length_of_number(const char s[], bool float_precision, bool plus_or_minus) /*ilosc znakow liczby*/
{
    /* flaga plus_or_minus mówi o tym, czy zezwalamy na znak poprzedzaj¹cy*/
    /* flaga float_precision mówi o tym, czy pozwalamy na umieszczenie znaku kropki w liczbie*/
    int i = 0;
    if(s[0] == '+' || s[0] == '-') /* jeœli user umieœci³ na pocz¹tku plus lub minus, sprawdzenie zaczynamy od indeksu 1*/
    {
        if(!plus_or_minus) return 0; /* jeœli nie pozwalamy na poprzedzaj¹cy liczbê znak*/
        i = 1;
    }
    for(; i < strlen(s); ++i)
    {
        if(isdigit(s[i]) || (float_precision == true && s[i] == '.')) continue;
        else break;
    }
    return i;
}

bool sprawdz_kanoniczna(const char s[])
{    /* wzorzec jest taki: +/-liczba rzeczywista, znak +/-,j, liczba rzeczywista*/
    int licznik;
    char temp[100] = "";
    int length = get_length_of_number(s, true, true);

    licznik = 5 + length;
    if(strlen(s) == length){
	return true;}

    strcpy(temp, s+length); /* w zmiennej 'temp' zostawiamy ci¹g znaków wejœciowy z wyciêt¹ pierwsz¹ liczb¹*/

    strcat(temp, "");

    if(temp[0] != '+' && temp[0] != '-' && temp[0] != 'j')
    {   int i; i = 0;
        printf("Blad: ");
        while(i<licznik){printf(" "); i++;}
        printf("^");
        return false;
    }
    if(temp[0] != 'j' && temp[1] != 'j')
    {   int i; i = 0;
        printf("Blad: ");
        while(i<licznik){printf(" "); i++;}
        printf("^");
        return false;
    }

	if (temp[0] == 'j'){
   	length = get_length_of_number(temp+1, true, false);
   	licznik++;}
	if (temp[1] == 'j'){
	length = get_length_of_number(temp+2, true, false);
	licznik=licznik+2;}

	licznik=licznik+length;
    if((((temp[0] == '-' || temp[0] == '+')) && strlen(temp) != length + 2) || (temp[0] == 'j' && strlen(temp) != length + 1))
    {
        int i; i = 0;
        printf("Blad: ");
        while(i<licznik){printf(" "); i++;}
        printf("^");
        return false;
    }
    return true;
}

bool sprawdz_wykladnicza(const char s[])
{
    /*wzorzec +-liczba rzeczywista, exp, nawias, +-j, liczba rzeczywista, pi, nawias*/
    int licznik;
    char temp[100] = "";
    int length;
    length = get_length_of_number(s, true, true);
    licznik = 5 + length;

    strcpy(temp, s+length);
    strcat(temp, "");

    if(temp[0] != 'e')
    {   int i; i = 0;
        printf("Blad: ");
        while(i<licznik){printf(" "); i++;}
        printf("^");
        return false;
    }
    licznik++;
    if(temp[1] != 'x')
    {int i; i = 0;
        printf("Blad: ");
        while(i<licznik){printf(" "); i++;}
        printf("^");
        return false;
    }
    licznik++;
     if(temp[2] != 'p')
    {int i; i = 0;
        printf("Blad: ");
        while(i<licznik){printf(" "); i++;}
        printf("^");
        return false;
    }
    licznik++;
    if(temp[3] != '(')
    {int i; i = 0;
        printf("Blad: ");
        while(i<licznik){printf(" "); i++;}
        printf("^");
        return false;
    }
    licznik++;
    if((temp[4] != '+') && (temp[4] != '-') && (temp[4] != 'j')){
         int i; i = 0;
        printf("Blad: ");
        while(i<licznik){printf(" "); i++;}
        printf("^");
        return false;
        }

    if(temp[4] == 'j'){

	length = get_length_of_number(temp+5, true, false);

    licznik += length;

	if(temp[(5+length)] != 'p'){
        int i; i = 0;
        printf("Blad: ");
        while(i<licznik){printf(" "); i++;}
        printf("^");
        return false;}
        licznik++;
    if(temp[(6+length)] != 'i'){
        int i; i = 0;
        printf("Blad: ");
        while(i<licznik){printf(" "); i++;}
        printf("^");
        return false;}
        licznik++;
    if(temp[(7+length)] != ')'){
        int i; i = 0;
        printf("Blad: ");
        while(i<licznik){printf(" "); i++;}
        printf("^");
        return false;}
        licznik++;
    if((((temp[0] == '-') || (temp[0] == '+')) && (strlen(temp) != length + 5)) || ((temp[0] == 'j') && (strlen(temp) != length + 4)))
        {int i; i = 0;
        printf("Blad: ");
        while(i<licznik){printf(" "); i++;}
        printf("^");
        return false;
        }
    } else{
    if((temp[4] == '+') || (temp[4] == '-')){
        licznik++;
        if (temp[5] != 'j'){
        int i; i = 0;
        printf("Blad: ");
        while(i<licznik){printf(" "); i++;}
        printf("^");
        return false;}

	length = get_length_of_number(temp+6, true, false);
	licznik += length;


    if(temp[(6+length)] != 'p'){
        int i; i = 0;
        printf("Blad: ");
        while(i<licznik){printf(" "); i++;}
        printf("^");
        return false;}
    licznik++;
    if(temp[(7+length)] != 'i'){
        int i; i = 0;
        printf("Blad: ");
        while(i<licznik){printf(" "); i++;}
        printf("^");
        return false;}
    licznik++;
    if(temp[(8+length)] != ')'){
        int i; i = 0;
        printf("Blad: ");
        while(i<licznik){printf(" "); i++;}
        printf("^");
        return false;}

    if((((temp[0] == '-') || (temp[0] == '+')) && (strlen(temp) != length + 6)) || ((temp[0] == 'j') && (strlen(temp) != length + 5)))
        {
        int i; i = 0;
        printf("Blad: ");
        while(i<licznik){printf(" "); i++;}
        printf("^");
        return false;
        }
	}
    }
    return true;
}


int main(){
char liczba[] = "";
printf("liczba 1: ");
scanf("%s", liczba);

    if (postac(liczba)==kanoniczna){
    if (sprawdz_kanoniczna(liczba)) printf("ok");
    else printf("nie ok");}
    else if(sprawdz_wykladnicza(liczba)) printf("ok");
    else printf("nie ok");

return 0;
}

Może ktoś będzie w stanie znaleźć błąd... 

komentarz 24 listopada 2015 przez draghan VIP (106,230 p.)
Całkiem fajnie wymyśliłaś sposób wskazywania błędu. :)

Ja już na dziś jestem zmęczony, a i jutro mam zajęcia od rana do wieczora, więc pewnie nie zajrzę.

Segmentation fault to błąd, związany zazwyczaj z przekroczeniem zakresu tablicy i próbą zapisania tam czegoś. Przypatrz się uważnie indeksom tablic.

Co ciekawe, dla identycznych danych wejściowych jak na screenie, Twój program u mnie działa poprawnie.
komentarz 24 listopada 2015 przez Delebrith Początkujący (250 p.)
Nie wymyśliłam - wymóg prowadzącego laborki ;)
Indeksy sprawdziłam, rozrysowałam to sobie na kartce i teoretycznie jest ok...

A jakiego używasz kompilatora? I na jakim systemie?
komentarz 24 listopada 2015 przez draghan VIP (106,230 p.)
Kompilator gcc z zestawu GCC 5.2, na Linux Mint 17.1 KDE (3.13.0-37 kernel). :) Jeśli jutro będę miał siłę żeby to na spokojnie przejrzeć, to Ci pomogę. :)

Dobrej nocy.
komentarz 24 listopada 2015 przez adrian17 Ekspert (344,100 p.)

Pierwszy błąd na samym początku programu (nie patrzyłem dalej):

char liczba[] = "";
scanf("%s", liczba);

Musisz zaalokować większą tablicę znaków, aby zmieścić wejścia użytkownika. Nie musisz też jej inicjalizować do pustego stringa.

komentarz 24 listopada 2015 przez draghan VIP (106,230 p.)
Reszta jest raczej w porządku. ;)

Podobne pytania

0 głosów
0 odpowiedzi 230 wizyt
pytanie zadane 21 października 2017 w Matematyka, fizyka, logika przez ELyyE Początkujący (320 p.)
+1 głos
3 odpowiedzi 3,913 wizyt
0 głosów
5 odpowiedzi 498 wizyt
pytanie zadane 17 kwietnia 2016 w C i C++ przez Azholi Początkujący (260 p.)

92,454 zapytań

141,263 odpowiedzi

319,099 komentarzy

61,854 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Akademia Sekuraka 2024 zapewnia dostęp do minimum 15 szkoleń online z bezpieczeństwa IT oraz dostęp także do materiałów z edycji Sekurak Academy z roku 2023!

Przy zakupie możecie skorzystać z kodu: pasja-akademia - użyjcie go w koszyku, a uzyskacie rabat -30% na bilety w wersji "Standard"! Więcej informacji na temat akademii 2024 znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...