Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

Rozdział 15 Zad. domowe - Kalkulator | sprawdzenie kodu

Ostatnio zmodyfikowano 2017-01-31 18:43
Autor Wiadomość
rambosek
Temat założony przez niniejszego użytkownika
Rozdział 15 Zad. domowe - Kalkulator | sprawdzenie kodu
» 2017-01-29 14:58:13
Witam wszystkich serdecznie. Niedawno zacząłem się uczyć programowania, i chciałbym aby ktoś z doświadczonych forumowiczów ocenił mój kod i wyjaśnij mi dokładniej niektóre funkcje w moim kodzie.
Powiedzmy że jest to pierwsza "poważniejsza" praca domowa, nad którą trzeba było już dłuższą chwilę spędzić.

Przede wszystkim nie jestem pewny, czy dobrze zrozumiałem treść zadania domowego, brzmi tak:

 - Napisz prosty kalkulator, który będzie potrafił dodawać, odejmować, mnożyć i dzielić. Program ten ma działać następująco:
1. Wypisuje obecny wynik
2. Wprowadź liczbę
3. Wybierz działanie (jeżeli liczba różna od 0)
4. Wykonaj obliczenia (jeżeli liczba różna od 0)
5. Wróć do kroku 1.
6. Jeżeli wprowadzoną liczbą jest 0, zakończ program.

- Zabezpiecz wcześniej napisany kalkulator przed podawaniem niepoprawnych liczb i operacji. Wykorzystaj wiedzę zdobytą z pierwszego zadania pracy domowej niniejszego rozdziału. Zabezpiecz również w analogiczny sposób przed możliwością wyboru nieprawidłowego działania.

Przede wszystkim zastanawiają mnie punkty 2, 3 i 4. W 2 punkcie "Wprowadź liczbę" sugeruje że mam wprowadzić 1 liczbę, napisałem kalkulator tak aby od razu wprowadzać 2 liczby. Ponadto nie rozumiem w kolejnych punktach tych nawiasów (jeżeli liczba różna od 0). Przecież można wykonywać wszystkie działania z 0, poza dzieleniem przez 0.

W każdym razie czy ktoś mógłby ocenić mój kod, ewentualnie nanieść jakieś poprawki? Chciałbym jeszcze zapytać o zabezpieczenie przed podaniem niepoprawnych liczb i operacji. Użyłem do tego funkcji sprawdzającej strumień wejścia cin.good(), a jej wynik zapisałem w zmiennej bool. Bez zmiennej cała pętla przy wprowadzaniu liter mi się zapętlała w kółko nie mam pojęcia dlaczego, tak samo jak nie ma funkcji cin.clear() oraz cin.sync(). Ponadto nie rozumiem czemu program zwraca mi 0 i kończy działanie, jak przy wyborze operacji (dodawanie, odejmowanie itp.) wpisze literę zamiast cyfry, komunikat wyświetla poprawnie że nie ma takiej opcji w menu, ale kończy działanie programu. Czy ktoś mógłby dokładnie wytłumaczyć mi działanie funkcji cin.clear() oraz cin.sync()? Jak się domyślam, program działa odczytując kolejne wiersze więc nie rozumiem, czemu to działa w przypadku, kiedy obie te funkcje są przed wprowadzaniem danych.

Wklejam kod i pozdrawiam :)


C/C++
#include <iostream>

using namespace std;

int main()
{
    float a, b;
    int dzialanie;
    float wynik = 0;
    bool spr;
   
    do
    {
        cin.clear();
        cin.sync();
       
        cout << "Wynik = " << wynik << endl;
        cout << endl << "Wprowadz 2 liczby rozdzielone spacjami: ";
        cin >> a >> b;
        spr = cin.good();
       
        if( spr == 1 )
        {
           
            cout << endl << "[1] Dodawanie \n[2] Odejmowanie \n[3] Mnozenie \n[4] Dzielenie \n[5] Zakoncz" << endl << endl;
            cout << "Wybierz dzialanie: ";
           
            cin.clear();
            cin.sync();
            cin >> dzialanie;
           
            switch( dzialanie )
            {
            case 1: wynik = a + b; cout << a << " + " << b << endl;
                break;
               
            case 2: wynik = a - b; cout << a << " - " << b << endl;
                break;
               
            case 3: wynik = a * b; cout << a << " * " << b << endl;
                break;
               
            case 4: if( b != 0 ) { wynik = a / b; cout << a << " / " << b << endl; }
                else cout << endl << "Nie dzielimy przez 0 LAMUSIE!" << endl;
               
                break;
               
            case 5: cout << endl << "Dzieki za skorzystanie z kalkulatora!" << endl;
                return 0;
               
            default: cout << endl << "Nie ma takiej opcji w menu!" << endl;
                break;
            }
           
        } else cout << "Wprowadz poprawne liczby" << endl;
       
    } while( dzialanie != 0 );
   
    return 0;
}

P-157099
latajacaryba
» 2017-01-29 15:30:09
Przede wszystkim zastanawiają mnie punkty 2, 3 i 4. W 2 punkcie "Wprowadź liczbę" sugeruje że mam wprowadzić 1 liczbę, napisałem kalkulator tak aby od razu wprowadzać 2 liczby. Ponadto nie rozumiem w kolejnych punktach tych nawiasów (jeżeli liczba różna od 0). Przecież można wykonywać wszystkie działania z 0, poza dzieleniem przez 0.
 
Myślę, że chodzi tu o
switch
'a, jeśli użytkownik poda liczbę 0, to program ma się zakończyć. Jeśli jest różna od zera, to oznacza to:
C/C++
switch( liczba ) {
case 1:
    // kod dodawania
    break
   
case 2:
    // kod odejmowania
    break;
   
    // i tak dalej...
   
case 0:
    cout << "dziekuje za skorzystanie z programu!";
    exit( 0 );
   
   
}

Oczywiście w //kod dodawania pobierasz dane od uzytkownika (co ma byc dodane). Przynajmniej tak ja to rozumiem.

Co do Twojego kodu:
- wolę, kiedy kalkulator podaje mi wynik PO wykonaniu obliczeń :D
C/C++
cout << "Wynik = " << wynik << endl; // po uruchomieniu wyswietla 0
Jak to naprawić? Ano, zaimplementuj ten fragment kodu dopiero po obliczeniach (po zakończeniu klamer switch'a)
C/C++
default: cout << endl << "Nie ma takiej opcji w menu!" << endl;
break;
}
cout << "Wynik = " << wynik << endl;
} else cout << "Wprowadz poprawne liczby" << endl;

- program ma opcję wyjścia za pomocą klawisza 5, dlatego usuń to:
do { } while( dzialanie != 0 );
 i wstaw w miejsce tej pętli
while( 1 ) { }
 while(1) oznacza: wykonuj w nieskończoność. Poza tym zmień opcję wyjścia z programu z 5 na 0 (jak w poleceniu).
P-157100
rambosek
Temat założony przez niniejszego użytkownika
» 2017-01-29 20:42:08
Dziękuje bardzo za sugestie :)

jednakże wciąż nurtuje mnie to, jak działają funkcje cin.good() oraz cin.sync(); czy na pradę jest sens stosować te funkcje przed każdym wczytywaniem danych? Tzn. widze na przykładzie że jest ale dalej nie wiem jak to dokładnie działa ;P

wklejam poprawiony kod, czy teraz jest okej? dodałem IFa sprawdzającego poprawność wprowadzonych danych przy wyborze działania, tylko mam pytanie:
Jak zrobić gdy w przypadku wprowadzenia litery przy wyborze działania, wymuszało ponowne wybranie opcji działania, a nie wraca aż do wprowadzenia liczb do obliczenia ponownie.
Pozdrawiam

C/C++
#include <iostream>

using namespace std;

int main()
{
    float a, b;
    int dzialanie;
    float wynik = 0;
   
    do
    {
        cout << "Wprowadz 2 liczby rozdzielone spacjami: ";
       
        cin.clear();
        cin.sync();
        cin >> a >> b;
       
        if( cin.good() )
        {
           
            cout << endl << "[1] Dodawanie \n[2] Odejmowanie \n[3] Mnozenie \n[4] Dzielenie \n[0] Zakoncz" << endl << endl;
            cout << "Wybierz dzialanie: ";
           
            cin.clear();
            cin.sync();
            cin >> dzialanie;
           
            if( cin.good() )
            {
               
                switch( dzialanie )
                {
                case 1: wynik = a + b; cout << a << " + " << b << endl;
                    break;
                   
                case 2: wynik = a - b; cout << a << " - " << b << endl;
                    break;
                   
                case 3: wynik = a * b; cout << a << " * " << b << endl;
                    break;
                   
                case 4: if( b != 0 ) { wynik = a / b; cout << a << " / " << b << endl; }
                    else cout << endl << "Nie dzielimy przez 0 LAMUSIE!" << endl;
                   
                    break;
                   
                case 0: cout << endl << "Dzieki za skorzystanie z kalkulatora!" << endl;
                    return 0;
                   
                default: cout << endl << "Nie ma takiej opcji w menu!" << endl;
                    break;
                }
                cout << "Wynik = " << wynik << endl;
               
            } else cout << "Nie ma takiej opcji w menu!" << endl;
           
        } else cout << "Wprowadz poprawne liczby" << endl;
       
    } while( 1 );
   
    return 0;
}
P-157109
karambaHZP
» 2017-01-29 21:09:02
Właśnie to zadanie ma ci uświadomić, po co są te metody.
Więcej można doczytać tutaj.
Z metodą
std::cin.sync()
 byłbym ostrożny w stosowaniu do czyszczenia bufora strumienia,
bo jest to jej efekt uboczny, a nie główna właściwość.
P-157110
latajacaryba
» 2017-01-29 22:18:32
jednakże wciąż nurtuje mnie to, jak działają funkcje cin.good() oraz cin.sync(); czy na pradę jest sens stosować te funkcje przed każdym wczytywaniem danych?
Akurat tego kursu nie czytałem (uczę się z książek), a obsługa strumieni jest trochę później. Jednak coś tam wiem ;)
Wiesz zapewne, co się stanie, kiedy poprosisz użytkownika o coś takiego:
C/C++
cout << "Podaj swoje imie" << endl;
cin >> imie;
cout << "Podaj imie matki\n";
cin >> imie2;
cout << "twoje imie to " << imie << " a imie Twojej Mamy to " << imie2;

W takim przypadku, gdy użytkownik przez np. nieuwagę poda przy pierwszym cin>>imie np. Jarosław Kaczyński, to:
w buforze będą 2 c-stringi (2 ciągi znaków) to pierwszy (Jarosław) zostanie przypisany do zmiennej imie. Tylko pierwsze.
Natomiast drugi nadal będzie siedział w buforze. teraz wyświetli się prośba o podanie imienia Matki, ale nie będziemy mieli
czasu na wpisanie, gdyż zostanie wykorzystany string z buforu (Kaczyński). Efektem będzie:
"twoje imie to "Jarosław" a imie Twojej Mamy to Kaczyński"
Właśnie po to czyści się bufor funkcją cin.sync()

Co do kodu:
- za dużo if'ów. Przykład:
C/C++
if( cin.good() )
{...} else cout << "Nie ma takiej opcji w menu!" << endl;

//Zamiast tego, użyj sobie - ja to nazywam - bramki logicznej

if( cin.good() == false ) //ta funkcja zwraca true lub false
     cout << "Nie ma takiej opcji w menu!";

Nie ma sensu z takiego powodu brać całego kodu w klamry. Im więcej klamer i zagnieżdżeń tym trudniej potem się połapać
Jednak to chyba Cię nie zadowala, bo pisałeś
Jak zrobić gdy w przypadku wprowadzenia litery przy wyborze działania, wymuszało ponowne wybranie opcji działania, a nie wraca aż do wprowadzenia liczb do obliczenia ponownie.
Zrób to więc tak:
C/C++
cout << endl << "[1] Dodawanie \n[2] Odejmowanie \n[3] Mnozenie \n[4] Dzielenie \n[0] Zakoncz" << endl << endl;
cout << "Wybierz dzialanie: ";
//edycja kodu
do { // petla do{..}while. Zawsze wykonuje sie za pierwszym razem, bez wzgledu na warunek. Przy nastepnych wykonaniach warunek jest juz sprawdzany. Wiecej w kursie na tej stronie
    cin.clear(); // tu wlasnie bedzie nam potrzebne czyszczenie buforu ;)
    cin.sync(); // tego tez uzyjemy
    cin >> dzialanie;
    if( cin.good() == false ) // jesli podalismy np. znak to...
         cout << "zle podana liczba! sprobuj ponownie\n"; //... powiedz uzytkownikowi co o tym myslisz. Jesli if() ma TYLKO JEDNA INSTRUKCJE, to nie musimy uzywac klamer.Tu instrukcja cout
   
} while( cin.good() == false ); // wykonuj dopoki cin.good() == false
// koniec edycji kodu
switch( dzialanie )
To samo zrób z if'em, zamien go na pętle do{}while. Poniżej wkleje jak powinien wyglądać kod, ale spróbuj najpierw sam go naprawić (mam na myśli: zmień tego if'a na tą pętle do...while.)


KOD
C/C++
int main()
{
    float a, b;
    int dzialanie;
    float wynik = 0;
   
    do
    {
       
        cout << "Wprowadz 2 liczby rozdzielone spacjami: ";
        do {
            cin.clear();
            cin.sync();
            cin >> a >> b;
            if( cin.good() == false )
                 cout << "zle podana liczba! sprobuj ponownie\n";
           
        } while( cin.good() == false );
       
       
       
        cout << endl << "[1] Dodawanie \n[2] Odejmowanie \n[3] Mnozenie \n[4] Dzielenie \n[0] Zakoncz" << endl << endl;
        cout << "Wybierz dzialanie: ";
        do {
            cin.sync();
            cin >> dzialanie;
            if( cin.good() == false )
                 cout << "zle podana liczba! sprobuj ponownie\n";
           
        } while( cin.good() == false );
       
        switch( dzialanie )
        {
        case 1: wynik = a + b; cout << a << " + " << b << endl;
            break;
           
        case 2: wynik = a - b; cout << a << " - " << b << endl;
            break;
           
        case 3: wynik = a * b; cout << a << " * " << b << endl;
            break;
           
        case 4: if( b != 0 ) { wynik = a / b; cout << a << " / " << b << endl; }
            else cout << endl << "Nie dzielimy przez 0 LAMUSIE!" << endl;
           
            break;
           
        case 0: cout << endl << "Dzieki za skorzystanie z kalkulatora!" << endl;
            return 0;
           
        default: cout << endl << "Nie ma takiej opcji w menu!" << endl;
            break;
        }
        cout << "Wynik = " << wynik << endl;
       
    } while( 1 );
   
    return 0;
}



P-157113
rambosek
Temat założony przez niniejszego użytkownika
» 2017-01-30 00:03:35
Baaardzo dziekuje za pomoc :)
@latajacaryba naprowadziles mnie na lepsze rozwiazanie, rzeczywiscie lepiej jest zrobic wiecej petli, program jest wydajniejszy dzieki temu. Moze IFów w twoim przykładzie nie jest mniej, tyle że nie ma klamer i kod jest czytelniejszy. I jeszcze taka uwaga, jak testowałem twój kod, przy wyborze działania jak wpisałem literkę, to zapętlał się komunikat o tym żeby spróbować ponownie. Tam w pętli masz samo cin.sync() bez clear. Jak dodałem clear to już ten problem nie występuje i kalkulator działa bardzo dobrze. Dziękuje jeszcze raz ;)
P-157114
karambaHZP
» 2017-01-30 00:51:37
Nie ma sensu z takiego powodu brać całego kodu w klamry.
Też tak kiedyś myślałem. Pisz klamry. Kiedyś możesz potrzebować dopisać drugą instrukcję do wykonania w ifie,
a zapomnisz dostawić klamry i kłopot gotowy.

Im więcej klamer i zagnieżdżeń tym trudniej potem się połapać
Im więcej klamer - nie.
Im więcej zagnieżdżeń - tak. Już przy drugim zagnieżdżeniu powinno się rozważyć wydzielenie kodu
z najbardziej zagnieżdżonego bloku do osobnej funkcji.

Tytaj możesz zerknąć jak używa się sprawdzania poprawności wprowadzanych danych oraz czyści strumień.
https://4programmers.net/C/FAQ​/Zabezpieczenie_przed_wpisywaniem_liter

if( b != 0 )
 Nie powinno się porównywać liczb zmiennoprzecinkowych operatorami
==
 oraz
!=
.
P-157115
latajacaryba
» 2017-01-30 10:47:24
Rambosek faktycznie, sprawdzalem co sie stanie jak nie dodam clear i zapomnialem potem dolaczyc tej funkcji do kodu:p sorry
P-157117
« 1 » 2
  Strona 1 z 2 Następna strona