Problem z konstrukcją if, else i while

0

O tuż mam taki kod:(program ma za zadanie przydzielić daną liczbę do czterech przedziałów: liczb, ujemnych, jednocyfrowych, dwucyfrowych i pozostałych. Jest to zadanie z Tutorala C++ Xion'a)

void main()
{
	int nKoniec;
  
   while(nKoniec != 1)
   {
	int nLiczba;
	cout << "Podaj dowolna liczbe calkowita: " ;
	cin >> nLiczba;

	if (nLiczba < 0)
	{
		cout << "ta liczba jest ujemna" << endl ;
	}
   
        if  (nLiczba >-10 && nLiczba <10)
	{
	     cout << "Ta liczba to liczba jednocyfrowa" << endl;		 
	}

	else if  ((nLiczba >-100 && nLiczba <-10) || (nLiczba < 100 && nLiczba >10))
	{
		 cout << "Ta liczba to liczba dwucyfrowa" << endl;		 
	}

	else
	{
		cout << "Ta liczba nie zalicza sie do zadnego z tych przedzialow" << endl; 
	}
	
	cout << "wpisz 1 jezeli chcesz zakończyć" << endl;
	cin >> nKoniec;
	
   }	
getch();
} 

Miałem i mam z tym programem dużo problemów. Trochę kombinowałem i doszedłem do takiego kodu, a teraz do rzeczy:(mam kilka pytań)

  1. Gdy wpisze liczbe mniejszą od -99 to program wyświetla mi że to liczba ujemna i że nie zalicza się do żadnego z tych przedziałów
    co mam zrobić by tak nie było?
  2. Dlaczego gdy kod while(nKoniec != 1) zastąpie while(nKoniec == 1) to program się włącza ale nic nie wyświetla?

Z góry dziękuje.

1
#include<iostream>
using namespace std;
int main(){
int nKoniec;

   while(nKoniec != 1)
   {
        int nLiczba;
        cout << "Podaj dowolna liczbe calkowita: " ;
        cin >> nLiczba;

        if (nLiczba < 0)
        {
                cout << "ta liczba jest ujemna" << endl ;
        }

        if  (nLiczba >-10 && nLiczba <10)
        {
             cout << "Ta liczba to liczba jednocyfrowa" << endl;
        }

        else if  ((nLiczba >-100 && nLiczba <-10) || (nLiczba < 100 && nLiczba >10))
        {
                 cout << "Ta liczba to liczba dwucyfrowa" << endl;
        }

        else
        {
                cout << "Ta liczba nie zalicza sie do zadnego z tych przedzialow" << endl;
        }

        cout << "wpisz 1 jezeli chcesz zakończyć" << endl;
        cin >> nKoniec;

   }
}

Jak chcesz wypisać liczbę np. -105, to program wyrzuci, że ta liczba nigdzie się nie zalicza ponieważ nie masz zdefiniowanej opcji dla liczb 3cyfrowych.

  1. Bo nie masz ustawionej zmiennej nKoniec na 1. Zrób
int nKoniec=1;

i później w pętli zmień na while(nKoniec==1){}

, będzie działać.
0

Wielkie dzięki,lecz kod który mi podałeś jest zły :) W Końcu po 2 dniach bez komputera dostałem się do niego ;) I zadałem sobie pytanie dlaczego nie moge rozwiązać takiego banalnego zadania? W końcu spojrzałem troche inaczej na kod i udało się. O to kod i moje już ostatnie 2 pytania (jesli za dużo "głupich" pytań to przepraszam, ale ja chce po prostu się dobrze nauczyć żeby póżniej nie robić błędów)

 void main()
{
	int nKoniec = 1;
  
   while(nKoniec == 1)
   
   {
	int nLiczba;
	cout << "Podaj dowolna liczbe calkowita: " ;
	cin >> nLiczba;

	if (nLiczba < 0)
	
               cout << "ta liczba jest ujemna" << endl;

	if  (nLiczba >-10 && nLiczba <10)
	
	    cout << "Ta liczba to liczba jednocyfrowa" << endl;

	if  ((nLiczba >-100 && nLiczba <-10) || (nLiczba < 100 && nLiczba >10))
	
	    cout << "Ta liczba to liczba dwucyfrowa" << endl;

           if (nLiczba > 99)

	     cout << "Ta liczba nie zalicza sie do zadnego z tych przedzialow" << endl;
			
	cout << "wpisz 1 jezeli chcesz kontynuowac";
	cin >> nKoniec;
}
_getch();
}
  1. Moim zdaniem mój kod jest zrobiony troche na "siłe", gdy np musiałbym zrobic wiecej przedziałów to taki kod na samych if'ach raczej nie wyjdzie. Więc czy dobrze napisałem ten kod czy nie? Czy może nie jest to ważne:)?
  2. Dlaczego musze przypisać do nKoniec 1 jeżeli i tak go na końcu kodu podaje?
1

Zacznę od końca:

Derton napisał(a)
  1. Dlaczego musze przypisać do nKoniec 1 jeżeli i tak go na końcu kodu podaje?

Bo w pętli while od razu testujesz, czy zmienna nKoniec jest równa 1. Przy pierwszym wywołaniu pętli while też jest to testowane. Jeśli nKoniec byłaby równa innej wartości niż 1 (tj. jeśli nie wstawilibyśmy tam jedynki), to ciało pętli while nie zostałoby wykonane ani razu. I użytkownik nie zobaczyłby żadnego testu.

W takim przypadku można użyć pętli do-while zamiast while -- wstawię to do kodu na końcu tego posta.

Derton napisał(a)
  1. Moim zdaniem mój kod jest zrobiony troche na "siłe", gdy np musiałbym zrobic wiecej przedziałów to taki kod na samych if'ach raczej nie wyjdzie. Więc czy dobrze napisałem ten kod czy nie? Czy może nie jest to ważne:)?

To jest ważne. Ważne jest, że Cię to obchodzi, że nie podoba Ci się kod mający wiele warunków i że szukasz sposobów na uproszczenie go. Dobrzy, profesjonalni programiści są dobrzy w pisaniu kodu, który wygląda prosto i przejrzyście. Dobrze napisany kod składa się ze stosunkowo prostych, jasnych funkcji i gdy go czytasz, to nie mówisz "wow, jakim cudem autor kodu to ogarnia!", tylko "ale to proste!". Oczywiście, w praktyce ciężko napisać większy program, który by nie był miejscami trochę pogmatwany. Przeważnie to kwestia kompromisów. Np. kod czytelniejszy może być sporo dłuższy od mniej czytelnego.

Generalnie, jeśli zadanie polega podaniu, że liczba należy do zera lub więcej klas (gdzie klasa to jakiś zakres liczb), struktura tego kodu jest w miarę prawidłowa. Gdy tych klas jest kilka, ciężko tu wymyślić coś mądrzejszego niż kilka if-ów.

Mogę Ci podsunąć kilka pomysłów na polepszenie czytelności. To nie są bezwzględne rady, tj. jeśli napiszę, że możesz tu zrobić X, to nie znaczy, że zawsze gdy można zrobić X, to trzeba to zrobić. To będzie jakiś kompromis. Programista musi w każdym przypadku odwołać się do własnego doświadczenia, oceny, czy do standardów kodowania narzuconych w projekcie.

Co do samych warunków, to przy testowaniu zakresów polecam rozważenie wprowadzenia kolejności takiej jak na osi liczbowej i stosowanie tylko operatora <. Najłatwiej zrozumieć to na przykładzie.

Gdy sprawdzasz, czy liczba mieści się w zakresie (-10..10), to nie robisz tego tak jak teraz:

n >-10 && n < 10

tylko tak:

-10 < n && n < 10

Widzisz, o co chodzi? To prawie jak zapis matematyczny: -10 < n < 10. Tylko w C++ (i innych językach też) musisz użyć normalnego operatora && i powtórzyć nazwę zmiennej n dwukrotnie. Zauważ jednak, że powyższe dwa zapisy robią to samo -- różnica tylko w klarowności.

Podobnie możesz zmienić ten skomplikowany test:

(nLiczba >-100 && nLiczba <-10) || (nLiczba < 100 && nLiczba >10)

na takie coś:

(-100 < n && n < -10) || (10 < n && n < 100)

Podejrzewam, że nie bardzo podoba Ci się ostatni warunek, który masz w programie -- ten sprawdzający, czy liczba nie została przyporządkowana do któregokolwiek zakresu. Sprawdzasz tam, czy liczba jest większa niż 99. To w zasadzie taki losowy warunek -- zapewne trudno znaleźć do niego odniesienie w treści zadania. No i co jeśli dojdzie Ci tam kolejny warunek? Albo dwa? Albo któryś z warunków się zmieni? Będziesz musiał nie tylko zmienić/dodać warunki, ale też posiedzieć dość długo nad tym ostatnim warunkiem. Bo może już nie będzie tak, że liczba nie spełnia żadnego z warunków wtedy i tylko wtedy, gdy jest większa niż 99.

Dlatego warto pomyśleć o dodaniu zmiennej będącej tzw. flagą, ew. zmienną stanu/statusu. Nazwij tę zmienną np. czyZakresPrzyporzadkowany. Umówimy się, że na początku pętli damy tej zmiennej wartość false. Gdy tylko przyporządkujemy liczbę do jakiegoś zakresu, to damy zmiennej czyZakresPrzyporzadkowany wartość true. Na końcu pętli sprawdzimy, czy zakres został przyporządkowany do liczby ;). Jeśli nie, to wypiszemy, że liczba nie należy do żadnego z przedziałów.

Do przejrzystości sporo daje wydzielenie funkcji. Mam nadzieję, że potrafisz tworzyć funkcje?

Zauważ, że przeprowadzasz kilka testów. Sprawdzasz, czy liczba jest parzysta, czy jest dodatnia itp. Takie testy, "czy pewne dane są XXX", to dobre kandydaty na funkcje. Szczególnie gdy testy są skomplikowane. Nazwy funkcji powinny odpowiadać temu, co sprawdzają. Np. funkcja sprawdzająca, czy liczba całkowita jest parzysta, powinna zwracać typ boolean (prawda/fałsz), przyjmować jeden argument typu int (sprawdzana liczba całkowita) i powinna mieć nazwę w stylu czyParzysta.

Po wprowadzeniu wszystkich zmian, kod wygląda to tak:

#include <iostream>

using namespace std;

// to tylko deklaracje funkcji -- definicje są na dole
bool czyUjemna(int n);
bool czyJednocyfrowa(int n);
bool czyJednocyfrowa(int n);
bool czyDwucyfrowa(int n);


int main()
{
  int nKoniec;
  do
  {
    int nLiczba;
    cout << "Podaj dowolna liczbe calkowita: " ;
    cin >> nLiczba;

    bool czyZakresPrzyporzadkowany = false;

    if (czyUjemna(nLiczba)) {
      cout << "ta liczba jest ujemna" << endl;
      czyZakresPrzyporzadkowany = true;
    }

    if  (czyJednocyfrowa(nLiczba)) {
      cout << "Ta liczba to liczba jednocyfrowa" << endl;
      czyZakresPrzyporzadkowany = true;
    }

    if  (czyDwucyfrowa(nLiczba)) {
      cout << "Ta liczba to liczba dwucyfrowa" << endl;
      czyZakresPrzyporzadkowany = true;
    }

    if (!czyZakresPrzyporzadkowany) {
      cout << "Ta liczba nie zalicza sie do zadnego z tych przedzialow" << endl;
    }

    cout << "wpisz 1 jezeli chcesz kontynuowac";
    cin >> nKoniec;
  } while (nKoniec == 1)
  _getch();
  return 0;
}

bool czyUjemna(int n) {
  return n < 0;
}


bool czyJednocyfrowa(int n) {
  return -10 < n && n < 10;
}

bool czyDwucyfrowa(int n) {
  return (-100 < n && n < -10) || (10 < n && n < 100);
}

Sam oceń, czy tak jest mniej czy bardziej czytelnie. (Żeby to ocenić obiektywnie, musisz oczywiście znać np. definiowanie własnych funkcji) Mam nadzieję, że kod mniej-więcej działa, bo tylko wkleiłem go na Codepad w ciut innej wersji żeby sprawdzić czy nie ma błędów kompilacji, a nie testowałem tego w ogóle (tj. wszystkie przekształcenia robiłem "w pamięci" i bez testów, co jest ZUE)

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