Sztuka programowania

Programowanie wielu kojarzy się z czymś niezmiernie skomplikowanym, zarezerwowanym wyłącznie dla informatyków – pasjonatów, którzy uczyli się go wiele lat nim posiedli odpowiednie umiejętności. Liczba powstających w ostatnim czasie aplikacji (choćby na telefony komórkowe) mówi nam jednak co innego.

Programistów jest sporo i wielu z nich nabyło tej umiejętności w przeciągu kilku miesięcy, bądź nawet tygodni. Sam osobiście znam osobę, która trafiła do pracy jako programista mimo, iż nie miała większego pojęcia o tym czym jest program, by po kilku dniach / tygodniach projektować skomplikowane aplikacje na potrzeby firmy. Nie jest to więc nauka wymagająca wielu lat praktyki. Jedyne czego potrzeba to odrobiny chęci i nieco innego podejścia, niż te stosowane w rzeczywistym świecie. Program bowiem wykona się dokładnie tak jak mu karzemy, ale nie będzie myślał za nas i jeżeli popełnimy nawet najdrobniejszy błąd (np. zapomnimy lub pomylimy znak) – nie będzie działał poprawnie lub wcale się nie uruchomi. W tym kursie mam zamiar przedstawić ci zasady i sposób tworzenia oprogramowania, który możesz później przełożyć na dowolny język. Sama nauka poleceń to drobiazg znając te podstawowe zasady i dysponując odpowiednim źródłem z listą i opisem instrukcji.

Naszą naukę programowania zaczniemy od odrobiny teorii.

Czym jest program?

W uproszczeniu to zbiór instrukcji nakazujących komputerowi lub dowolnej innej maszynie działanie zgodnie z jakimś przeznaczeniem. Przykładowy abstrakcyjny program w naszym życiu będzie wyglądał tak:

Program ZRÓB KAWĘ

1. Jeżeli siedzisz to wstań
2. Idź do kuchni
3. Nalej wody do czajnika
4. Uruchom czajnik
5. Otwórz szafę
6. Wyjmij kawę z szafy
7. Połóż kawę na stole
8. Zamknij szafę
9. Nasyp 2 łyżeczki kawy do filiżanki
10. Poczekaj, aż woda się zagotuje
11. Zalej kawę wodą

Zauważ, że każdy z tych elementów możemy jeszcze bardziej rozdrobnić. Przykładowo punkt 2: Idź do kuchni. Mimo, iż dla nas ludzi jest to logiczne musimy mieć zakodowane gdzie i co to jest kuchnia oraz jak się chodzi. Zarówno jednak w naszym przypadku, jak i w komputerach zwykle nie musimy przejmować się jak coś działa dogłębnie, gdyż mamy od tego odpowiednie instrukcje stworzone już przez kogoś innego. Funkcja (instrukcja) w języku komputerowym związana z chodzeniem gdziekolwiek będzie wyglądał podobnie jak ta:

Idź_do (kuchnia)

Idź do nazywa się funkcją (lub poleceniem, instrukcją, procedurą) i oznacza jakieś zadanie do wykonania, a kuchnia w tym przypadku to tzw. argument funkcji (polecenia, instrukcji, procedury) i opisuje szczegóły wykonania tego zadania (w tym przypadku miejsce gdzie mamy iść). Oczywiście funkcja idź_do może mieć inne argumenty np.:

Idź_do (pokój)
Idź_do (toaleta)

Może mieć też tych argumentów wiele..:

Idź_do (jadalnia, pokój, toaleta)

Powyższa funkcja może nas pokierować najpierw do jadalni, później do pokoju, a na końcu do toalety. To czy tak się stanie zależy od osoby, która wspomnianą funkcję stworzyła (napisała). Najczęściej będziemy szukać takich funkcji, które spełnią nasze zadania – czytać ich opisy i próbować dopasować do naszego celu. Sami oczywiście również będziemy takie funkcje tworzyć.

Spójrzmy na przykład innej funkcji, która służy do otwierania czegoś:

Otwórz (szafa)

Proces otwierania czegoś można przedstawić następująco

Funkcja Otwórz (coś)

1. Zlokalizuj (coś)
2. Podnieś (ręka)
3. Skieruj (ręka, w_kierunku (coś) ))
4. Chwyć (klamka, coś)
5. Pociągnij_do siebie
6. Cofnij (ręka)

Jeżeli teraz za „coś” podstawimy np. szafa to „program” otworzy szafę, jeżeli drzwi – otworzy drzwi. Zamiast jednak pisać 6 instrukcji za każdym razem gdy chcemy coś otworzyć, piszemy jedną Otwórz (coś). Dlatego pisze się właśnie swoje funkcje – by przyspieszyć tworzenie programu – w szczególności jeżeli w naszym programie będziemy często wykonywać jakąś czynność. Oczywiście funkcja Otwórz też składa się z jakiś funkcji i te również moglibyśmy rozdrobnić. W końcu doszlibyśmy do procesów sterowania ręką za pomocą sygnałów wysyłanych z mózgu. W przypadku komputerów mózgiem jest procesor.

Rozwińmy nieco temat funkcji i spójrzmy na kolejny przykład z funkcją Uruchom (silnik), która ma za zadanie przekręcenie kluczyka w samochodzie w celu odpalenia silnika.

czy_uruchomiony = Uruchom (silnik)

Powyższa funkcja próbuje uruchomić silnik i przekazuje rezultat wykonania tej próby do tzw. zmiennej „czy_uruchomiony„. Zmienna to pewna nazwa (etykieta), która przechowuje jakąś wartość. W tym przypadku zmienna może przyjąć np. 2 wartości – TAK lub NIE. Zmienne są bardzo często używane podczas pisania programów, gdyż pozwalają nam sprawdzać i ponownie wykorzystywać pewne dane. Tutaj funkcja Uruchom (silnik) przekazuje informacje czy został on uruchomiony do zmiennej o nazwie czy_uruchomiony. Taka informacja przydaje nam się do określenia co robimy dalej. Jeżeli zmienna będzie miała wartość TAK, możemy nacisnąć pedał gazu i jechać. Jeżeli zaś NIE – możemy np. spróbować ponownie. Poniższy przykład to ilustruje:

czy_uruchomiony = Uruchom (silnik)
Jeżeli (czy_uruchomiony = NIE) -> czy_uruchomiony = Uruchom (silnik)
Jeżeli (czy_uruchomiony = TAK) -> Naciśnij (gaz)

W powyższym przykładzie sprawdzamy co zawiera zmienna czy_uruchomiony. Jeżeli będzie zawierać NIE, wykonamy ponownie polecenie Uruchom (silnik) i ponownie przekażemy informacje do zmiennej czy_uruchomiony. Jeżeli będzie zawierać TAK – naciśniemy gaz. Powyższy przykład w najgorszym wypadku będzie próbował 2 razy uruchomić silnik i jeżeli mu się to powiedzie to ruszymy samochodem (naciśniemy gaz). Jak możemy przypuszczać po nawiasach – jeżeli jest również funkcją. Jednak jest to specjalna funkcja, która ma za zadanie sprawdzić coś i ewentualnie wykonać jakąś czynność. Tego typu funkcję nazywa się instrukcją warunkową – jeżeli jakiś warunek zostanie spełniony – wykona się, jeżeli zaś nie to wykona albo coś innego, albo nic nie wykona. Spójrzmy poniżej:

Jeżeli (warunek prawdziwy) ZRÓB COŚ (warunek nieprawdziwy) ZRÓB COŚ

Warunek to inaczej sprawdzenie czy coś jest prawdziwe lub nie np.

Jeżeli (2>4) Napisz ("2 jest większe od 4") [w przeciwnym wypadku] Napisz ("2 jest mniejsze od 4")

W powyższym przykładzie funkcja Jeżeli sprawdza czy 2 jest większe od 4-ch. Jeżeli tak wyświetli 1-szy komunikat. Jeżeli nie – wyświetli drugi komunikat. 2<4 to jest właśnie tzw. warunek funkcji. Warunek to sprawdzenie „Czy PRAWDĄ jest, że 2 > 4?”. W tym przypadku odpowiedzią będzie NIE (to jest tzw. FAŁSZ). Napis „2 jest większe od 4” pojawi się tylko jeżeli odpowiedzią będzie TAK (czyli tzw. PRAWDA).

Oczywiście warunki mogą być różne. Poniżej kilka przykładów

Czy 2<3?
Czy 2>8?
Czy 30+4-999 = -89?
Czy (30+4=34) i (4+30=34)?

Ostatni przykład pokazuje, że warunki można ze sobą łączyć. W tym przypadku sprawdzamy czy suma 30 i 4 daje 34. Jeżeli tak – sprawdzamy czy suma 4 i 30 = 34. Jeżeli to również prawda to wtedy cały warunek jest prawdziwy i dopiero wówczas wykona się to co zaplanowaliśmy. Inny przykład

Czy (30+3=34) lub (30+4=34)

Powyżej sprawdzamy czy którykolwiek z tych warunków jest prawdziwy. Jeżeli któraś z tych sum wynosi 34 wtedy cały warunek jest prawdziwy i wówczas wykona się to co chcemy.

Wróćmy do programu uruchamiającego samochód. Próbowaliśmy uruchomić silnik 2 razy. Jeżeli jednak ani razu nam się nie uda to nie ruszymy. Rozwiązaniem byłoby próbować kilka razy lub w nieskończoność, aż w końcu się uda. Gdybyśmy chcieli to robić tradycyjną metodą musielibyśmy napisać szereg funkcji:

czy_uruchomiony = Uruchom (silnik)
Jeżeli (czy_uruchomiony = NIE) -> czy_uruchomiony = Uruchom (silnik)
Jeżeli (czy_uruchomiony = NIE) -> czy_uruchomiony = Uruchom (silnik)
Jeżeli (czy_uruchomiony = NIE) -> czy_uruchomiony = Uruchom (silnik)
Jeżeli (czy_uruchomiony = NIE) -> czy_uruchomiony = Uruchom (silnik)
Jeżeli (czy_uruchomiony = NIE) -> czy_uruchomiony = Uruchom (silnik)
Jeżeli (czy_uruchomiony = NIE) -> czy_uruchomiony = Uruchom (silnik)
Jeżeli (czy_uruchomiony = NIE) -> czy_uruchomiony = Uruchom (silnik)
Jeżeli (czy_uruchomiony = NIE) -> czy_uruchomiony = Uruchom (silnik)
Jeżeli (czy_uruchomiony = NIE) -> czy_uruchomiony = Uruchom (silnik)
Jeżeli (czy_uruchomiony = TAK) -> Naciśnij (gaz)

W powyższym przykładzie program w najgorszym wypadku będzie próbował 10 razy uruchomić silnik. A co jeżeli chcemy próbować coś 100, albo 1000 razy? Pisanie 100 lub 1000 linijek mija się z celem. Aby wykonać jakąś czynność n-tą ilość razy stosujemy tzw. pętle. Pętla to inaczej zbiór instrukcji, które będą tak długo powtarzane, aż pewien ustalony warunek nie zostanie spełniony. Przykładowo

PODCZAS_GDY (czy_uruchomiony = NIE)
WYKONUJ czy_uruchomiony = Uruchom (silnik)

Powyższa instrukcja sprawdza czy zmienna czy_uruchomiony zawiera NIE i jeżeli tak się dzieje próbuje uruchomić silnik. Po każdej próbie znowu sprawdza czy nadal jest NIE. Próba uruchomienia silnika będzie się wykonywała tak długo, aż zmienna czy_uruchomiony nie będzie zawierała innej wartości niż NIE – w tym przypadku tak długo, aż nie pojawi się TAK. Oczywiście może się zdarzyć, że samochód jest po prostu popsuty i wówczas program będzie próbował uruchomić silnik w nieskończoność. W miarę możliwości powinniśmy unikać takich sytuacji i zawsze ograniczać liczbę sprawdzeń. Tego typu błędy w programach często powodują zawieszenie się aplikacji – jeżeli jakiś program się zawiesza co jakiś czas bywa, że jest to spowodowane właśnie podobnym błędem – program wykonuje jakąś czynność w nieskończoność. Mówimy wówczas, że program się zapętlił.

uruchamiano = 0
PODCZAS_GDY ((czy_uruchomiony = NIE) LUB (uruchamiano < 10)
WYKONUJ czy_uruchomiony = Uruchom (silnik)  { oraz } uruchamiano = uruchamiano + 1

W powyższym przykładzie dołożyliśmy 2-gą zmienną i nazwaliśmy ją sobie uruchamiano. Ma ona za zadanie ograniczyć liczbę prób do 10. W pierwszym wierszu nadaliśmy zmiennej wartość 0. Podczas sprawdzania czy wykonać funkcję uruchamiania silnika, testujemy 2 rzeczy – czy zmienna czy_uruchomiony zawiera wartość NIE (tak jak było do tej pory) oraz czy zmienna uruchamiano ma wartość mniejszą od 10. Jeżeli tak jest to wykonywana jest wspomniana próba uruchomienia silnika. Po tej próbie (niezależnie od efektu).zwiększana jest wartość zmiennej uruchamiano. Dokładniej – zmienna uruchamiano dostaje wartość taką jaką ma powiększoną o 1. Jeżeli więc za pierwszym razem miała 0, teraz będzie miała 0+1, czyli 1. Za drugim razem 1+1, czyli 2.. za 3-cim – 2+1, czyli 3.. itd. Próby uruchomienia zakończą się wtedy jeżeli silnik zostanie uruchomiony LUB liczba uruchomień będzie równa 10 (zmienna uruchamiano nie będzie miała wartości mniejszej od 10 tylko równą lub większą).

Zmienną, która zwiększa swoją wartość cyklicznie (równo przez jakiś czas) – tak jak w przypadku uruchamiano = uruchamiano + 1 nazywa się iteratorem. Iterator służy do zliczania czegoś.

Podsumujmy to co już wiemy i dodajmy zarazem kilka nowych słówek do naszego zbioru.

JĘZYK – język posiada składnię oraz zbiór funkcji, których możemy używać pisząc program. My nie używaliśmy żadnego konkretnego języka do tej pory do pisania programu – używaliśmy tzw. pseudojęzyka.

KOD PROGRAMU – treść, zapis tekstowy naszego programu – wiersz po wierszu – zbiór funkcji, zmiennych itp. Wszystko co pisaliśmy było kodem pseudojęzyka stworzonego na potrzeby tej publikacji.

FUNKCJA – kod programu, który wykonuje jakieś zadanie np. uruchamia silnik. Może zwracać jakąś wartość do zmiennej – przekazywać rezultat wykonanania na zewnątrz (np. czy_uruchomiony = Uruchom (silnik). Funkcja próbuje uruchomić silnik i przekazuje (zwraca) informacje (wartość) TAK lub NIE do zmienej czy_uruchomiony

ARGUMENT – jakaś wartość przekazywana do funkcji, która jest potrzebna do jej wykonania np. w funkcji IDŹ_DO (pokój), pokój jest argumentem funkcji IDŹ_DO. Funkcja IDŹ_DO wymaga, aby określono gdzie chcemy iść. Jeżeli nie podamy tego to nigdzie nie pójdziemy – zadanie / funkcja się nie wykona – program zwróci najczęściej błąd. Funkcje nie muszą mieć argumentów – to czy mają i ile leży w geście programisty, który daną funkcję stworzył

PROCEDURA – to samo co funkcja, ale nie może zwracać wartości

MAKRO – podobne jak procedura. Działa nieco szybciej, zużywa więcej pamięci i nie może otrzymywać żadnych argumentów.

ZMIENNA – pewna nazwa, która przechowuje jakąś wartość (np. liczbę, tekst, TAK/NIE…). Wartość tą możemy zmieniać dowolnie w programie (możemy przypisać to co chcemy zmiennej wiele razy w trakcie działania programu – stąd nazwa zmienna – coś co zmienia swoją wartość np. piszemy uruchamiano = 0 (zmienna ma wartość 0), i za chwilę uruchamiano = 0+1 (zmienna ma wartość 1)

TYP ZMIENNEJ – rodzaj wartości jaką przechowuje zmienna. Typem może być np. liczba całkowita (np. 102), liczba zmiennoprzecinkowa (np. 102.324), tekst (np. „ALA MA KOTA”), znak (np. 'C’)itd. Mówimy często o zmiennych przechowujących jakiś rodzaj wartości (typ), że są to zmienne tekstowe (zawierają jakiś napis), znakowe (zawierają pojedyńczy znak), czy np. liczbowe. Inny typ to tzw, zmienne bool-owskie (bool / boolean) – zawierają TAK lub NIE (PRAWDA / FAŁSZ lub 0 / 1). Mówimy o typie zmiennej dlatego, że wiele języków (nie wszystkie) wymaga, abyśmy podczas tworzenia zmiennej podali jakiego rodzaju (typu) informacje będzie ona przechowywała. Tworząc zmienną liczbową nie możemy jej przypisać np. napisu „ALA MA KOTA”. Podobnie jak tworząc zmienną tekstową nie możemy jej przypisać liczby (np. 102).

STAŁA – w uproszczeniu to to samo co zmienna, ale ta nie może zmieniać swojej wartości. W przeciwieństwie do zmiennej, stała otrzymuje raz jakąś wartość. Wykorzystuje się ją, by skrócić zapis lub by coś ustawić np. jeżeli potrzebujemy kilka razy pisać w naszym programie „ALA MA KOTA” to możemy sobie utworzyć stałą o nazwie przykładowo AMK i przypisać do niej wspomniany tekst. Później (podobnie jak w przypadku zmiennych), zamiast pisać całe słowo – piszemy tylko AMK, a program zrozumie, że w tym miejscu ma wstawić „ALA MA KOTA”. Oczywiście nic nie stoi na przeszkodzie byśmy użyli do tego zmiennej, ale czasami przez głupi błąd możemy zmienić jej wartość i uzyskamy nie to co chcieliśmy. Przy próbie zmiany wartości stałej – program wyrzuci nam błąd.

WARUNEK – sprawdzenie czy coś jest równe, większe, mniejsze itp. (np. czy 2>3?, czy 3+1=4? czy zmienna liczba_PI = 3,14?) Jeżeli w odpowiedzi uzyskujemy TAK mówimy, że warunek jest spełniony (jest prawdziwy), jeżeli zaś NIE – wówczas warunek nie jest spełniony (jest fałszywy). W programowaniu zamiast TAK/NIE używa się notacji PRAWDA/FAŁSZ lub 1/0.

INSTRUKCJA WARUNKOWA – funkcja sprawdzająca czy warunek jest spełniony i wykonująca określone zadanie zależnie od tego. Jeżeli PRAWDĄ jest, że 2>4 wykonaj jakąś funkcję, w przeciwnym wypadku (czyli jeżeli FAŁSZEM jest, że 2>4), wykonaj inną funkcję.

PĘTLA – zbiór funkcji, które mają zostać wykonane ileś razy. Dzięki pętlą nie musimy tworzyć przykładowo 100 funkcji, które wykonują jakąś czynność. Możemy taką funkcję napisać raz i umieścić ją w pętli, nadając warunek by ta wykonywała się 100 razy. Pętla za każdym razem zanim wykona to co ma sprawdza czy warunek jest spełniony – podobnie jak w instrukcji warunkowej. Jeżeli tak jest (warunek ma wartość PRAWDA), wówczas wywoła to co zapisane wewnątrz niej. Jeżeli jest odwrotnie to zakończy swoje działanie. Pętla, której warunek zawsze będzie spełniony nazywa się pętlą nieskończoną. Przykładowo: PODCZAS_GDY (0=0) jedź. W tym przykładzie pętla sprawdza czy 0=0 i jeżeli tak to wykonuje funkcję jedź. Oczywiście 0 jest równe 0 zawsze (warunek jest spełniony – prawdziwy, PRAWDA czyli 1), więc funkcja jedź będzie wykonywała się w nieskończoność. Przykładowo PODCZAS_GDY (licznik < 10) licznik = licznik+1. Przyjmując, że zmienna licznik na początku miała wartość 0, w powyższym przykładzie pętla wykona się 10 razy. Zmienna licznik będzie zwiększała swoją wartość, aż osiągnie 10 i wtedy warunek licznik < 10 nie będzie spełniony (bo 10 nie jest mniejsze od 10). Pętla nie wykona kolejnego zliczania tylko zakończy swe działanie.

OPERATOR – operatorami nazywa się symbole < > = I LUB itp. Są to znaki (ciągi znaków) służące do tworzenia warunków. Operator mniejszy niż to <, większy niż to >, mniejszy lub większy to <>, równy = itd. Także LUB, czy I są w wielu językach operatorami, bo one również dokonują pewnego sprawdzenia. Wynikiem działania operatorów jest zawsze TAK / NIE (PRAWDA / FAŁSZ) np. 2>4 – wynikiem jest NIE (FAŁSZ). 4>2 I 33>88 – wynikiem jest również FAŁSZ, bo choć 4 jest większe od 2-ch to 33 nie jest większe od 88, a w przypadku operatora I wymagamy, aby oba warunki były prawdziwe. 4>2 LUB 33>88 – tutaj wynikiem jest PRAWDA, gdyż mimo, iż 33 nie jest większe od 88, to jednak 4 jest większe od 2-ch, a w przypadku LUB wymagamy, aby tylko jeden z warunków był prawdziwy.

ITERATOR – umowna nazwa na zmienną, której głównym zadaniem jest stałe zwiększanie / zmniejszanie swojej wartości np. o 1. Iterator to inaczej licznik. Wielu programistów nazywa takie zmienne po prostu i np. i = i+1. Oczywiście to jak nazwiemy zmienną zależy tylko od nas.