Komputer ma wyręczać człowieka w różnych żmudnych pracach, np. rachunkach. Tym, który ma zagonić go do roboty jest programista. Spróbujmy przeanalizować prosty przykład, ilustrujący typowe problemy występujące przy programowaniu praktycznych zadań. Nasze zadanie jest niezwykle proste : obliczyć objętość kuli o promieniu r = 55.
Oczywiście można to zrobić jedną instrukcją : PRINT 55^3 * 3.14 / 3 lub w Pascalu, który popieramy znacznie bardziej niż BASIC : WriteLn (55 * 55 * 55 * 3.14 / 3); *)
Tylko czy warto w ogóle włączać komputer? Chyba szybciej policzymy to na papierze. Użycie komputera mogłoby być opłacalne gdybyśmy potrzebowali policzyć objętości wielu różnych kul, ale z kolei powyższy "program" w ogóle się do takiego zadania nie nadaje! Dla każdego nowego promienia musimy pisać treść instrukcji od nowa. Żeby to poprawić musimy program rozbudować. Zamiast wartości promienia użyjmy zmiennej R, której wartość będzie wczytywana :
ReadLn(R); WriteLn(R * R * R * 3.14 / 3); **)
Wykonanie tej sekwencji to wczytanie wartości promienia i wydruk objętości kuli o tym promieniu. Po tym program kończy działanie - aby policzyć następną kulę trzeba go uruchomić ponownie. Jest to znaczna niedogodność, którą usuniemy korzystając z instrukcji pętli :
R := 1;
While R<>0 Do
Begin
ReadLn(R);
If R<>0 Then WriteLn(R * R * R * 3.14 / 3);
{ proponuję zastanowić się po co instrukcja }
{ warunkowa zamiast po prostu WriteLn(...) }
End;
Zauważmy, że zakończenie pracy następuje po podaniu przez użytkownika liczby 0 jako wartości promienia. Po zakończeniu obliczeń dla jednych program nie wdaje się w dyskusję co robić dalej tylko wczytuje następne dane co przyśpiesza wykonywanie obliczeń. Równocześnie łatwo go zakończyć, podając wartość, dla której wykonywanie obliczeń i tak nie ma większego sensu.
Wszystko to działa bardzo pięknie, dopóki z programu korzysta osoba znająca jego działanie, niestety jednak reszta świata nie ma większych szans na dogadanie się z nim. Poprawimy to dodając kilka komunikatów i napiszemy ostateczny program :
Program Kula; { Oblicza i drukuje objętość kuli o zadanym promieniu }
Var R : Integer;
Begin
R := 1;
While R<>0 Do
Begin
WriteLn;
WriteLn('Obliczanie objętości kuli.');
Writeln('Podaj promień, wartość 0 kończy pracę');
ReadLn(R);
If R<>0 Then WriteLn('Objętość kuli o promieniu',R,'wynosi',R*R*R*3.14/3);
End;
End.
Jest to świetny program (grunt to skromność), ma jednak poważną wadę: zupełnie nie sprawdza poprawności danych wejściowych. A przecież nie istnieją kule o ujemnym promieniu, więc liczenie ich objętości nie ma sensu! Trzeba więc zmienić strukturę pętli tak, aby przy podanym ujemnym promieniu zamiast liczenia i wydruku objętości wykonało się :
WriteLn('Złe dane - powtórz.');
Niezbędne poprawki proponuję wprowadzić samodzielnie. Uważny Czytelnik zauważył już pewnie, że dopuszczamy się tutaj poważnego marnotrawstwa. Przecież w momencie wypisywania komunikatu "Złe dane" wiemy co w tych danych jest złe, ale nie informujemy o tym użytkownika, zmuszając go do zastanowienia się DLACZEGO dane są złe! Zastanowienia się zupełnie zbędnego, gdyż można przecież napisać :
WriteLn('Złe dane : promień nie może być ujemny. Powtórz');
Można tu ewentualnie dodać emisję sygnału dźwiękowego, dodatkowo zwracającego uwagę operatora : WriteLn(chr7)); Instrukcja ta działa poprawnie w Turbo Pascalu na IBM PC, nie mogę jednak zagwarantować, że tak samo będzie we wszystkich innych realizacjach Pascala - jest to przykład wprowadzenia do programu elementów zależnych od konkretnej wersji języka.
Wbrew pozorom, kontrola poprawności danych wejściowych nie jest wcale zabiegiem kosmetycznym, stanowiącym drobne upiększenie programu. W wielu zagadnieniach próba wykonania obliczeń dla niepoprawnych danych kończy się fatalnie - błędem wykonania i przerwaniem działania programu. Najprostszy przykład otrzymamy zastępując obliczanie objętości kuli przez obliczanie wartości pierwiastka kwadratowego procedurą SQRT. Przy czym przerwanie wykonywania jest i tak mniejszym złem, bo skoro program "padł", to nie dał żadnych wyników i od razu widać, że coś jest nie tak. Dużo gorzej byłoby, gdyby program do którego wszyscy mają zaufanie (gdyż był wielokrotnie sprawdzony) dał złe wyniki i wprowadził kogoś w błąd. PAMIĘTAJMY: dobry program nigdy nie daje złych wyników, nawet gdy dane są złe!
Po wprowadzeniu wszystkich poprawek mamy gotowy całkiem niezły produkt, który powinien zainteresować wszystkich ludzi odczuwających przemożną potrzebę obliczenia w wolnych chwilach objętości kul. Pomimo to, że takich osób jest zapewne niezbyt wiele nasz wysiłek nie musi iść na marne. Pokazaliśmy jakie kroki trzeba wykonać aby program rozwiązujący jeden konkretny problem (objętość jednej kuli) zmienić w program, który potrafi rozwiązać klasę problemów (objętość dowolnej kuli), i do tego może być wykorzystywany przez wielu użytkowników a nie tylko przez autora. Elementy podobne do pokazanych tutaj musi zawierać każdy program przeznaczony do praktycznych zastosowań.
Nie zawsze jednak uda się zrobić program, który bez żadnych zmian będzie zadowalał wielu odbiorców. Często zdarza się, że ktoś chce rozwiązać zadanie podobne do naszego, ale różniące się na tyle, że zastosowanie naszego programu wymaga modyfikacji treści tegoż programu. Takiego klienta jesteśmy w stanie zadowolić niewielkim kosztem (bez pisania dla niego programu od nowa) pod warunkiem, że nasze programy są modyfikowalne. Oznacza to, że łatwo do nich wprowadzić zmiany. A to z kolei zależy od wielu czynników: czy program jest napisany w sposób czytelny i przejrzysty, czy ma właściwą strukturę, czy zawiera komentarze***). Jeśli w programie występują stałe to warto nadać im nazwy (pozwala na to Pascal) lub na początku programu podstawić ich wartości na zmienne, tak aby w treści programu nie pisać wartości stałych tylko nazwy. Będzie to dużym ułatwieniem gdy trzeba zmienić wartość stałej występującej w wielu miejscach programu - w dobrym programie wystarcza zmiana linii, w której nadajemy wartość nazwie stałej (zmiennej). W naszym przypadku może to być : PI := 3.14; a następnie WriteLn (R * R * R * PI / 3);
Jeśli okaże się, że wyniki obliczeń są za mało dokładne, to wystarczy zmienić deklarację PI na PI := 3.1415; Niezależnie od tego w ilu miejscach korzystamy z wartości PI.
Druga możliwa sytuacja to ktoś zainteresowany naszym programem, ale dysponujący innym komputerem. Zamiast pisać od nowa program na jego komputer można spróbować przenieść działający program z naszego komputera. Będzie to tym łatwiejsze im mniej w tym programie instrukcji, które nie należą do podstawowego standardu języka, lecz są dostępne tylko na danej maszynie. Także odwoływanie się bezpośrednio do fizycznych adresów, czy też korzystanie z takich właściwości, których nie posiadają inne typy komputerów znacznie ogranicza możliwości przenoszenia programów.
Pora wreszcie podsumować nasze uwagi o programach. Wszystkie miały służyć realizacji jednej prostej idei : jeśli coś robisz, to rób tak, aby Twoja praca mogła być wykorzystana przez innych (lub może nawet przez Ciebie, przy rozwiązywaniu w przyszłości nowych zadań). Idea ta zbliża nas do wyjaśnienia tytułu artykułu, bowiem jednym z lepszych sposobów na zrobienie majątku jest sprzedanie tej samej rzeczy kilka razy. Własnie programy komputerowe idealnie nadają się na taki wielokrotny towar, oczywiście pod warunkiem, że są napisane tak aby mogły służyć wielu osobom.
*) Objętość kuli obliczamy ze wzoru 1/3 πr3, stosując mnożenie R * R * R zamiast nie istniejącego w Pascalu potęgowania.
**) Oczywiście nie jest to kompletny program - brakuje tu choćby linii "program" i "end", ale w razie potrzeby uzupełnienia tego typu z łatwością wykonacie to sami.
***) O wymienionych zagadnieniach pisaliśmy już trochę i zapewne jeszcze nie raz do nich wrócimy.
|
UWAGA BŁĄD Jak zapewne dostrzegli uważni Czytelnicy artykułu, wszystkie zamieszczone w tym artykule fragmenty programów liczących objętość kuli są BŁĘDNE!!!, gdyż dokonują obliczeń wg niepoprawnego wzoru. Oczywiście poprawny wzór na objętość kuli o promieniu R brzmi: V = 3/4 * πR3 Niby niewielki błąd we wzorze dyskwalifikuje cały program, może więc ten przypadek zachęci Was do lektury artykułu w całości poświęconego zagadnieniom poprawności programów, który ukaże się zaraz po odcinku "Systemu Operacyjnego". |
Andrzej Pilaszek - Bajtek 11/1987r.