Archiwum SQLBlog

MEGAWPIS: Wstęp do programowania

Poznaj tok myślenia programistów i programistyczne koncepcje na przykładach z życia wziętych.

Czyli: zacznij myśleć, zanim zaczniesz programować

Zawarte tu koncepcje będą miały zastosowanie (w większym lub mniejszym stopniu) do wszystkich współczesnych wysokopoziomowych języków programowania (takich jak Python, C#, Java, Ruby, JavaScript, PHP)

Po co i dla kogo powstał ten wpis?

“Cześć! Chcę programować. Od czego mam zacząć?” – Fora internetowe i fejsbukowe grupy aż uginają się od podobnych pytań. Na każde z nich przeważnie jest sporo odpowiedzi, ludzie doradzają początkującemu najlepsze (według nich) języki na start.

A jakby zacząć od zupełnie innej strony? Zamiast wybierać jakiś konkretny język (nad wyborem którego ludzie spędzają zdecydowanie zbyt wiele czasu) i planować sobie całą ścieżkę kariery, może lepiej zacząć od poznania podstawowych koncepcji wykorzystywanych w programowaniu, przedstawionych prostym językiem, z przykładami odnoszącymi się do codziennego życia? Żeby załapać “o co chodzi” i żeby późniejsza nauka wybranego języka przebiegała dużo sprawniej.

Dlatego właśnie powstał ten wpis. Ma on wprowadzić początkującego w odpowiedni tok myślenia, wykorzystywanego podczas programowania. Po takim wstępie dużo łatwiej będzie przyswoić sobie podstawy już wybranego, konkretnego języka, bo same koncepcje będą znajome.

Programowanie to operowanie abstrakcjami. To, co tu przedstawiam, to nie jest kurs programowania jako taki. To jest wyłożenie koncepcji, które spotkasz, gdy zaczniesz się uczyć konkretnego języka.

 

Czym jest programowanie?

Definicji programowania jest wiele, ale zobaczmy, co powie nam Wikipedia:

Programowanie komputerów – proces projektowania, tworzenia, testowania i utrzymywania kodu źródłowego programów komputerowych lub urządzeń mikroprocesorowych (mikrokontrolery). Kod źródłowy jest napisany w języku programowania, z użyciem określonych reguł, może on być modyfikacją istniejącego programu lub czymś zupełnie nowym. Programowanie wymaga dużej wiedzy i doświadczenia w wielu różnych dziedzinach, jak projektowanie aplikacji, algorytmika, struktury danych, języki programowania i narzędzia programistyczne, kompilatory, czy sposób działania podzespołów komputera. W inżynierii oprogramowania programowanie (implementacja) jest tylko jednym z etapów powstawania programu. Między programistami trwają nieustanne debaty, czy programowanie komputerów jest sztuką, rzemiosłem czy procesem inżynieryjnym. Bezpośrednią formą sztuki w tej dziedzinie jest demoscena.

Ja natomiast zdefiniowałabym programowanie jako rozwiązywanie problemów, przy czym problemy te są na różnym poziomie – np: Klient potrzebuje programu do fakturowania (problem ogólny, biznesowy). Jak zaprojektować taki program? (problem architektury systemu). Jakie klasy i metody będą potrzebne, by stworzyć działający program? (problem architektury, ale niższego poziomu). Jak napisać tą konkretną funkcjonalność? (problem techniczny, ogólny). Dlaczego to nie działa? (problem techniczny, szczególny).

Trzeba też pamiętać o tym, że komputer wykonuje komendy krok po kroku. I jest w tym bardzo dokładny. Dlatego też my, wydając mu polecenia, również musimy być dokładni. Maszyna będzie wykonywać zadane jej polecenia bardzo dosłownie – dlatego też trzeba nieco przestawić swoje codzienne myślenie by podążało bardziej precyzyjnymi ścieżkami.

 

Przechowywanie danych

Człowiek przechowuje w swojej głowie masę informacji. Jak powiem “wypłata”, to od razu zaświta ci kwota, którą dostajesz co miesiąc (albo którą chciałbyś dostawać). Jak powiem “Szarik”, to zobaczysz owczarka niemieckiego. Jak powiem “Garfield”, to zobaczysz grubego rudego kota. I tak dalej…

Z konkretnymi nazwami kojarzymy konkretne rzeczy. Nadajemy osobom i rzeczom nazwy, by łatwiej je identyfikować.

Komputer działa podobnie. On również przechowuje informacje pod pewnymi nazwami.

Sami możemy tworzyć nowe dane i je nazywać, a następnie wykorzystywać dalej w programie. Tak naprawdę, programowanie to w dużej mierze właśnie operowanie danymi (pobieranie, przetwarzanie, zapisywanie). Robi się to bardzo prosto. Wystarczy podać nazwę i przypisać do niej wartość, jaką ma reprezentować. Na przykład tak:

wypłata = 3000

Od teraz, jeśli dalej w programie będziemy chcieli operować na kwocie naszej wypłaty, to będziemy robić to za pomocą jej nazwy. Właśnie stworzyliśmy swoją pierwszą zmienną.

Czasami jest to porównywane do pudełek. Każde pudełko jest opisane (ma swoją nazwę), a jego zawartością są włożone tam przez nas dane. Mogą być też różne rodzaje pudełek – duże, małe, o specjalnych kształtach, albo uniwersalne, dostosowujące się. Dane, które wkładamy, muszą pasować do pudełka.

 

Typy danych

Komputer musi wiedzieć, jakiego typu jest wartość, na której chcemy wykonywać jakieś operacje. Po co mu to? Ano po to, żeby sprawdzić, czy to co chcemy zrobić, w ogóle jest możliwe. Dwa najpopularniejsze typy danych, z którymi się spotkasz na początku, to liczby i tekst. Komputer ma określone działania, które może wykonywać z liczbami, i inne, które może wykonywać na tekście.

Na przykład liczby może dodawać, dzielić je i mnożyć. Mogą być dodatnie albo ujemne. Mogą być całkowite lub ułamkowe.

Jeśli ma tekst, to może policzyć ilość znaków albo zmienić ich wielkość (duże litery na małe).

Czy może pomnożyć tekst przez inny tekst? Nie, to by było bez sensu. Co dostaniesz po pomnożeniu “Ala ma kota” przez “Lubię placki”?

Możesz zmienić “a” na “A”, ale nie możesz tego zrobić z cyframi.

I tutaj uwaga. Niektóre języki będą wymagały, by w momencie tworzenia nowej zmiennej określić, jakiego będzie typu (a więc musielibyśmy napisać liczba wypłata = 3000). Inne przyjmą wszystko, i dopiero w momencie gdy będziemy chcieli zapisanych danych użyć, komputer sprawdzi, jakiego są typu (wystarczy więc zwykłe wypłata = 3000).

Innym często spotykanym typem jest Prawda lub Fałsz. Oznacza to, że możemy mieć jeden z dwóch stanów – albo prawdę, albo fałsz. Trochę jak przełącznik włącz-wyłącz (i często są stosowane właśnie w takiej roli).

Co jeszcze oznaczają typy danych? Każdy typ zabiera pewną ilość miejsca w pamięci komputera. Warto więc dobierać typy tak, żeby jak najlepiej odpowiadały faktycznym potrzebom. Jeśli coś zmieści się do pudełka po butach, to po co wynajmować całego tira?

Ćwiczenia

Masz do dyspozycji kilka typów danych: liczba całkowita, liczba zmiennoprzecinkowa (z częścią ułamkową), tekst, data, data wraz z godziną, prawda/fałsz. Jaki typ nadałbyś poniższym danym?

  • Data urodzenia
  • Kwota wypłaty
  • Liczba stron w książce
  • Komunikat o błędzie
  • Jest wtorek
  • Zawartość alkoholu w piwie
  • Treść wiadomości
  • Termin wizyty u lekarza
  • Zmiany zapisane

 

A co, jeśli…?

W codziennym życiu podejmujemy masę decyzji – od małych (co zjeść na śniadanie?), po poważne (kupić dom czy mieszkanie?). Każda decyzja jest w pewien sposób uwarunkowana. Na przykład to, co zjemy na śniadanie, zależy od zawartości naszej lodówki.

Jeśli jest szynka, to zjem kanapkę z szynką, a jeśli nie, to z serem.

W programowaniu też bardzo często będziemy musieli podjąć decyzję na podstawie pewnych danych (czy coś jest równe czemuś innemu, czy coś jest od czegoś większe lub mniejsze, czy coś istnieje).

Żona mówi do męża programisty:

  • Idź do sklepu, kup chleb, a jak będą jajka, to weź dziesięć.

Mąż wchodzi do sklepu i pyta:

  • Dzień dobry, są jajka?
  • Są.
  • To poproszę dziesięć chlebów.

Każdy “normalny” człowiek wie, co miała żona na myśli, wysyłając męża do sklepu. Potrzebuje chleba, oraz 10 jajek (o ile będą). Natomiast mąż zinterpretował polecenie tak, jak zrobiłby to komputer: najpierw sprawdził, czy warunek został spełniony (Czy są jajka?), a następnie na tej podstawie podjął decyzję o zakupie chleba w odpowiedniej ilości.

Przeanalizujmy teraz przykład z kanapką. Moglibyśmy go zapisać tak:

Jeśli (czy jest szynka? = tak)

to zrób kanapkę z szynką

W przeciwnym przypadku, jeśli (czy jest ser? = tak)

zrób kanapkę z serem

W przeciwnym przypadku

idź do sklepu

Mniej więcej w ten właśnie sposób wygląda podejmowanie decyzji przez komputer. Sprawdza, czy spełniony jest warunek, i jeśli tak, to wykonuje zadane polecenia. Jeśli natomiast warunek nie jest spełniony, przechodzi dalej – do sprawdzenia kolejnego warunku, jeśli jest (może być ich wiele), lub do klauzuli “jeśli żaden warunek nie został spełniony, to zrób to…” (która jest opcjonalna – nie musi występować).

Spójrzmy na jeszcze kilka przykładów:

Jeśli (“A”)

to “a”

W przeciwnym przypadku, jeśli (“B”)

to “b”

W przeciwnym przypadku, jeśli (“C”)

to “c”

We wszystkich innych przypadkach

“nie znam takiej litery”

 

Czy pada deszcz?

Jeśli pada, to weź parasolkę.

Idź na spacer.

Zwróć uwagę, że bez względu na to, czy pada deszcz, idzie się na spacer. Deszcz determinuje tylko kwestię zabrania parasolki. Jeśli nie pada, to pomijasz krok zabierania parasolki i po prostu wychodzisz.

Podczas pobierania pieniędzy z bankomatu, system sprawdza, czy kwota o którą prosisz nie przekracza twojego stanu konta.

Jeśli (kwota < stan konta)

Wypłać gotówkę

W przeciwnym przypadku

“Niewystarczające środki”

Podczas logowania się do jakiegokolwiek serwisu następuje sprawdzenie, czy użytkownik podał prawidłowe hasło (o wymaganej liczbie znaków), czy podał swój login (czy pole nie jest puste lub nie zawiera niedozwolonych znaków), a następnie – czy dane logowania zgadzają się z zapisanymi w bazie (czy taki użytkownik w ogóle istnieje, a jeśli tak, to czy podał prawidłowe hasło).

Jak więc widzisz, takie warunki sprawdzane są bardzo często.

W praktyce często zdarza się, że warunek, który musimy sprawdzić, nie będzie taki prosty. Jeśli chcemy zrobić ciasto, musimy uwzględnić szereg składników.

Jeśli (Czy jest mąka? = tak i Czy jest mleko? = tak i Czy są jajka? = tak i Czy jest cukier? = tak)

Hurra, pieczemy ciasto!

W przeciwnym przypadku

Brakuje składników, nie będzie ciasta 🙁

Ciasto możemy upiec jedynie wtedy, gdy mamy wszystkie potrzebne składniki. Gdy któregoś zabraknie, nie możemy zabrać się za pieczenie. Ale zaraz! Przecież możemy iść do sklepu, prawda? No właśnie, możemy czy nie?

Jeśli (Czy jest mąka? = tak i Czy jest mleko? = tak i Czy są jajka? = tak i Czy jest cukier? = tak)

Hurra, pieczemy ciasto!

W przeciwnym przypadku,

Jeśli (Niedziela handlowa = tak)

Idź do sklepu

W przeciwnym przypadku

Brakuje składników, sklepy zamknięte, nie będzie ciasta 🙁

Warunki możemy ze sobą łączyć. Jeśli wszystkie muszą być spełnione, używamy “i” (tak jak w przykładzie z ciastem), a jeśli tylko jeden z kilku, to używamy “lub”.

Jeśli (Nie masz samochodu lub Stary samochód się bardzo popsuł)

Kup nowy samochód

 

Jeśli (login jest za krótki lub zawiera niedozwolone znaki)

Wyświetl odpowiedni komunikat

 

Jeśli (“A” lub “B” lub “C” lub “D”)

“To są litery”

W przeciwnym przypadku, Jeśli (1 lub 3 lub 5 lub 7)

“To są cyfry”

W przeciwnym przypadku

“Nie wiem, co to jest”

 

Możemy je też łączyć i mieszać na przeróżne sposoby:

Jeśli ((coś i coś) lub (coś i coś))

Zrób coś

 

Jeśli ((“A” lub “B”) albo (1 lub 3))

“To są litery lub cyfry”

Zwracam uwagę na nawiasy. Działają tak jak w matematyce – najpierw wykonywane są działania w nawiasach, od wewnętrznych do zewnętrznych. To znaczy, że najpierw rozpatrzony zostanie warunek (“A” lub “B”), następnie (1 lub 3), a dopiero na końcu warunek albo, który porówna wyniki dwóch wcześniejszych działań.

W programowaniu mówimy na to “instrukcje warunkowe”. By je definiować, używamy słów kluczowych takich jak if (jeśli), if else / else if (w przeciwnym przypadku – sprawdzamy inny warunek), oraz else (we wszystkich pozostałych przypadkach  / jeśli żaden z warunków nie zostanie spełniony). 

 

Ćwiczenia

Weź kartkę i ołówek. Spróbuj zrobić “symulator bankomatu”. Poniżej przedstawiam kroki potrzebne do pobrania gotówki, a Ty się zastanów – i zapisz na kartce – co w którym kroku należałoby sprawdzić. W jaki sposób poszczególne kroki są od siebie zależne? Co się stanie, jeśli coś pójdzie nie tak?

  • Włóż kartę
  • Podaj pin
  • Podaj kwotę
  • Zabierz kartę
  • Zabierz gotówkę

 

Prawda czy fałsz?

Są pytania, na które możemy odpowiedzieć krótko – tak lub nie.

Czy pada deszcz? Czy jestem głodny? Czy mam samochód? Czy lubię lody?

Pisząc programy, często będziemy korzystać ze sprawdzania, czy coś jest prawdą, czy nie (albo – czy coś w ogóle istnieje), na przykład podczas sprawdzania warunków Jeśli(…).

Czy plik, który zamierzam otworzyć, istnieje?

Czy użytkownik wpisał sześć cyfr, tak jak go prosiłem?

Czy dziś jest niedziela?

Czy mamy rok przestępny?

Czy 3 jest większe od 7?

Komputer działa zero-jedynkowo. Albo coś istnieje, albo nie. Albo coś jest prawdą, albo fałszem. Nie ma żadnego “to zależy”. A co się stanie, jeśli będziemy chcieli porównać prawdę z fałszem? Tu z pomocą przychodzi nam:

Algebra Boola

Nazwa pochodzi od nazwiska matematyka, George’a Boole’a. Nie będziemy się tu zagłębiać w szczegóły, weźmiemy tylko najprostsze przykłady z logiki dwuargumentowej (która jest przy okazji podstawą całej elektroniki). Może brzmi to strasznie, ale straszne nie jest.

Mamy dwie wartości: Zero (fałsz) i Jeden (prawda).

Mamy trzy możliwe działania: lub (coś albo coś), i (coś i coś) oraz nie (nie coś).

I teraz zobaczmy, co możemy z tym zrobić. Jakie będą wyniki porównań?

Jeśli (Pada deszcz i Świeci słońce)

Jest tęcza

Tęcza pokazuje się tylko wtedy, gdy oba warunki są spełnione. Gdy oba są prawdą.

Wniosek nr 1: prawda i prawda = prawda

 

Jeśli (Jesteś człowiekiem i Mamy rok 2174)

“Hmm…”

Czy jesteś człowiekiem? Tak. Czy mamy rok 2174? Nie. Czy oba warunki są prawdziwe? Nie.

Wniosek nr 2: prawda i fałsz = fałsz

 

Jeśli (Mieszkasz na Marsie i Mamy rok 2174)

“To podejrzane.”

Czy mieszkasz na Marsie? Nie. Czy mamy rok 2174? Też nie. A więc całe stwierdzenie jest fałszywe.

Wniosek nr 3:  fałsz i fałsz = fałsz

 

Jeśli (Masz kota lub Masz psa)

“Masz fajnego zwierzaka!”

Masz fajnego zwierzaka, bez względu na to, czy jest nim pies, czy kot.

Wniosek nr 4: prawda lub prawda = prawda

 

Jeśli (Polska leży w Afryce lub Polska leży w Europie)

“Polska leży w Europie lub Afryce.”

Czy Polska leży w Afryce? Nie. Czy leży w Europie? Tak. A więc leży tu lub tu? Tak.

Wniosek nr 5: prawda lub fałsz = prawda

 

Jeśli (Jesteś rybą lub Jesteś skorupiakiem)

“Jakim cudem to czytasz?!”

Jesteś rybą? Nie. A skorupiakiem? Też raczej nie. A więc nie jesteś ani jednym, ani drugim.

Wniosek nr 6: fałsz lub fałsz = fałsz

 

Jeśli (nie Prawda)

“Fałsz”

Jeśli (nie Fałsz)

“Prawda”

Jeśli warunek, który podamy, będzie prawdą, ale użyjemy zaprzeczenia, to stanie się fałszem. I w drugą stronę tak samo – jeśli zaprzeczymy fałsz, otrzymamy prawdę.

 

…no i jest jeszcze nic

Null jest specyficznym bytem. To nie jest ani prawda, ani fałsz. To jest… nic. Wartość pusta, nieokreślona, z niczym nie powiązana. Możesz myśleć o tym trochę jak o czarnej dziurze.

Czy 3 jest większe niż czarna dziura?

Czy tekst “Ala ma kota” jest równy czarnej dziurze?

Jeżeli przy próbie uruchomienia programu zobaczysz gdzieś błąd związany z Nullem, to będziesz wiedział, że oznacza to brak czegoś, co być powinno (komputer spodziewa się coś znaleźć, ale nie znajduje nic, więc wyrzuca błąd z nullem).

Nie będę dalej rozwijać tematu – każdy język ma swoje zasady jeśli chodzi o pracę z nullami, dlatego jak już się zdecydujesz na jakiś, to dowiedz się, jakie zasady w nim panują. Nie jest to może wiedza absolutnie niezbędna na początek, ale myślę że dobrze jest mieć świadomość istnienia takiego bytu.

Podsumowanie

W skrócie:

Jeżeli mamy łącznik i, to prawda jest tylko wtedy, gdy oba wyrażenia są prawdą.

Jeżeli mamy łącznik lub, to fałsz otrzymujemy tylko wtedy, gdy oba wyrażenia są fałszywe.

Jeżeli użyjemy zaprzeczenia, to otrzymamy wartość przeciwną.

To jeszcze raz:

1 i 1 = 1

1 i 0 = 0

0 i 0 = 0

1 lub 1 = 1

1 lub 0 = 1

0 lub 0 = 0

nie 1 = 0

nie 0 = 1

Ćwiczenia

Prawda czy fałsz?

  • ((prawda lub fałsz) i prawda) =
  • ((prawda i fałsz) lub prawda) =
  • ((prawda lub fałsz) i (prawda lub prawda)) =
  • ((prawda lub fałsz) lub (prawda i fałsz)) =
  • ((prawda lub fałsz) i (prawda i fałsz)) =
  • (((prawda lub fałsz) lub (prawda i prawda)) i (fałsz i fałsz)) =
  • ((prawda lub fałsz) lub ((prawda i prawda) i (fałsz i fałsz)) =

 

Cytat na każdy dzień

Wyobraź sobie, że w swoim programie chcesz mieć jeden motywujący cytat na każdy dzień roku. Gdzieś te cytaty trzeba przechowywać, by można było się łatwo do nich dostać. Wiesz, że dane są przechowywane w zmiennych, i wiesz też, że w tym przypadku będziesz potrzebował zmiennych typu tekst. Bardzo dużo zmiennych. Jeśli uważasz, że ręczne tworzenie ponad trzystu sześćdziesięciu zmiennych jest bez sensu, masz całkowitą rację.

A teraz pomyśl, co by się przydało. Na przykład takie jedno, zbiorcze pudło, z ponumerowanymi przegródkami. Żeby do każdej przegródki móc włożyć cytat, a potem brać tylko ten z odpowiednim numerem (odpowiadającym numerowi dnia w roku).

Na szczęście mamy taką strukturę dostępną. Wystarczy zadeklarować, że potrzebujemy 365 elementów typu tekst i już. Potem zostaje tylko wypełnienie pustych przegródek danymi.

Takie struktury nazywają się tablicami. Mają ustaloną z góry liczbę elementów (to znaczy my określamy, ile tych elementów ma być, i po utworzeniu tablicy ta liczba nie może się już zmienić). Standardowo numeracja elementów tablicy (czyli “przegródek”) zaczyna się od zera. Tak więc nasza tablica na cytaty, mająca 365 elementów, będzie miała numery od 0 do 364. Te numery nazywane są indeksami.

Co możemy przechowywać w tablicach? Wszystko. Liczby, tekst, daty, jak i bardziej złożone elementy. 

Zobaczmy:

Moje cytaty [365] [0] ”Cytat 1”

[1] ”Cytat 2”

[2] ”Cytat 3”

[3] ”Cytat 4”

[4] ”Cytat 5”

[5] ”Cytat 6”

…i tak dalej, aż do 364.

A teraz wyobraź sobie pociąg towarowy. Jest lokomotywa, a za nią ciągnie się kilkadziesiąt wagonów. Ile? Tyle, ile potrzeba do przewiezienia danej partii towaru. Jak brakuje, to się je doczepia, a przy nadmiarze odczepia. Można też odczepić jakiś wagon ze środka. Wygodne, prawda? Jeśli nie wiesz, ile rzeczy będziesz miał do przewiezienia, to taki pociąg będzie dobrym wyborem, bo można go dostosować.

W programowaniu “pociągiem do przewożenia danych” będą listy. Jest to rzecz bardzo podobna do tablic – też ma ponumerowane elementy – z tą różnicą, że lista może się dynamicznie zmieniać, gdy będziemy jej dokładać nowe elementy lub usuwać te zbędne.

Dwóch mężów poszło do sklepu. Każdemu z nich żona przygotowała listę zakupów.

Jeden dostał kartkę z wypisanymi na niej produktami.

Drugi dostał dużo małych karteczek, na każdej zapisany jeden produkt, który ma kupić.

W trakcie wkładania kolejnych rzeczy do koszyka, pierwszy mąż wykreślał kolejne punkty ze swojej listy. Drugi mąż natomiast wyrzucał zbędne karteczki.

Na końcu obaj zostali bez jednego produktu, bo w sklepie zabrakło mleka.

Różnica polega na tym, że pierwszy mąż miał w ręku całą, pokreśloną już, kartkę z listą zakupów. Produkty były w koszyku, ale nadal zajmowały miejsce na kartce.

Drugi mąż został z jedną tylko małą kartką w ręku – tą z napisem “mleko”. Reszty kartek nie ma, nie są już potrzebne.

Tak mniej więcej wygląda różnica między tablicą i listą. Jeśli stworzysz tablicę z 10 elementami, to ona przez cały czas będzie miała 10 elementów. Może być pusta, ale nadal zajmuje miejsce “za dziesięciu”. Listy są za to elastyczne. Możesz zacząć z 10 elementami, potem dokładać do niej kolejne (na przykład na skutek działań użytkownika), potem kilka usunąć, dołożyć jeszcze parę i będzie wszystko w porządku. Lista sama się dostosuje.

Każdy z nas widział kiedyś słownik albo encyklopedię. Wszyscy wiemy jak to działa – szukasz konkretnego słowa, a następnie sprawdzasz jego definicję lub tłumaczenie. Współcześnie prawdopodobnie częściej sięgamy do Wikipedii niż do tradycyjnych papierowych wydań, ale idea jest taka sama.

Pisząc program, również możemy skorzystać z takiego mechanizmu. Jest on podobny do listy, z tą różnicą, że o ile w liście mamy jeden element w każdym polu, o tyle słownik będzie miał dwa: klucz oraz wartość (w encyklopedii byłoby to słowo oraz jego definicja). Ta para klucz-wartość jest ze sobą nierozerwalnie związana. Jeśli czegoś szukamy w słowniku, szukamy po słowie kluczowym.

Zobaczmy, jak mógłby wyglądać przykładowy słownik:

Słownik symboli

!: wykrzyknik

@: małpa

$: dolar

%: procent

*: gwiazdka

Ćwiczenia

Czego użyłbyś do przechowywania przedstawionych poniżej danych? Tablicy, Listy czy Słownika?

  • Nazwy dni tygodnia
  • Lista zwycięzców w konkursie sportowym
  • Wykaz liter w angielskim alfabecie
  • Wykaz produktów w sklepie internetowym

 

Jeszcze raz!

Smażyłeś kiedyś naleśniki? Jeśli nie, to na pewno widziałeś, jak ktoś inny to robi. Nabiera porcję ciasta, wylewa na patelnię, smaży przez minutę, przewraca na drugą stronę, znowu smaży minutę, zdejmuje z patelni. A potem nabiera kolejną porcję ciasta, smaży naleśnika,  potem następnego, i jeszcze jednego. I powtarza się te czynności tak długo, jak długo mamy ciasto. Można by to ująć w ten sposób:

Dopóki (Jest ciasto? = tak)

Smaż naleśniki

 

Można to ująć w drugą stronę: smaż naleśniki, dopóki jest ciasto.

Smaż naleśniki

Dopóki (Jest ciasto?  = tak)

Myślisz, że to przecież na jedno wychodzi? Nie do końca.

W pierwszym przypadku najpierw sprawdzasz, czy jest ciasto, a potem dopiero smażysz naleśnika.

W drugim przypadku smażysz pierwszego naleśnika, a dopiero potem sprawdzasz, czy jest ciasto na kolejnego.

To, co właśnie opisałam, w programowaniu nazywamy pętlami. Dokładniej to pętla while w dwóch wariantach – jeden ze sprawdzaniem warunku uruchomienia pętli na początku, a drugi na końcu (po tym jak już pętla raz się uruchomi).

A teraz schody.

Wracasz do domu z pracy. Stajesz na klatce schodowej i z westchnieniem patrzysz na czekającą cię wspinaczkę. Masz do pokonania 47 schodów.

Programista ująłby to tak:

Od (Schodka nr 1) do (Schodka nr 47)

Idź do góry

Wiemy, ile jest schodów, więc możemy zaplanować wykonanie dokładnej ilości kroków.

W podobny sposób moglibyśmy przejrzeć nasze pudełko z cytatami z poprzedniego rozdziału:

Od (Przegródka nr 0) do (Przegródka nr 364)

Wyświetl cytat z bieżącej przegródki

To, co komputer robi, to przechodzi po kolei przez wszystkie przegródki, w każdej na chwilę się zatrzymując i odczytując zawarte tam dane. Dla ciebie przejrzenie takiej ilości “szufladek z danymi” może wydawać się nużące, ale komputer robi to w ułamku sekundy. Poza tym to maszyna, ona się nigdy nie nudzi.

To jest inny rodzaj pętli, nazywany pętlą for. Charakteryzuje się tym, że wykonuje określoną ilość kroków, po czym się kończy. Ilość kroków do wykonania jest określana na samym jej początku i musi być z góry zdefiniowana. 

Czy moglibyśmy wejść po schodach z wykorzystaniem pętli Dopóki? Tak, ale z pewną modyfikacją.

Pętla typu Dopóki sama z siebie nie liczy, ile kroków zrobiliśmy. Musimy tego pilnować sami. Gdybyśmy napisali:

Dopóki (Czy są schody? = tak)

Idź do góry

To weszlibyśmy na ostatnie piętro. A my mieszkamy dużo niżej, więc nie o to nam chodziło. Musimy zejść ze schodów w konkretnym momencie. Musimy więc zrobić tak:

Aktualny schodek = 1

Dopóki (Aktualny schodek <= 47)

Idź do góry

Dolicz +1 do Aktualnego schodka

Co tu się dzieje? Po kolei.

Najpierw definiujemy, od którego schodka zaczynamy wchodzić. Startujemy z parteru, więc zaczynamy od pierwszego.

Potem następuje sprawdzenie, czy schodek, na którym aktualnie stoimy, to ten, na którym chcieliśmy się znaleźć. Potrzebujemy być na 47 by zakończyć naszą wspinaczkę. Jesteśmy na 1. Jeden jest mniejsze od 47. A więc… do góry. Musimy tylko pamiętać o liczeniu schodów. Jak sprawdzisz, czy jesteś tam gdzie chcesz, jeśli nie liczysz kroków? Nie sprawdzisz, i będziesz szedł w górę w nieskończoność…

Idźmy dalej.

Pamiętasz pociąg towarowy z poprzedniego rozdziału? Ten z doczepianymi wagonami.

Co musielibyśmy zrobić, gdybyśmy chcieli zajrzeć do każdego wagonu i na przykład sprawdzić, czy jest pusty? Musielibyśmy iść wzdłuż pociągu i  po drodze kontrolować kolejne wagony. Nie wiemy, ile tych wagonów jest. Nie musimy. Będziemy wiedzieli, kiedy dojdziemy do końca.

Dla każdego (Wagon w Pociągu)

Sprawdź, czy jest pusty

To jest wariacja pętli for, nazywana for each. Różni się tym, że zamiast podawać ilość kroków do wykonania, podajemy jej kolekcję elementów, przez które ma przejść od początku do końca. Pętla sama wie, gdzie jest początek a gdzie koniec.

Co za dużo, to niezdrowo

Pętle są fajne i bardzo przydatne. Wiąże się jednak z nimi jeden problem, na który trzeba uważać, szczególnie na początku swoich programistycznych przygód. Otóż dość łatwo jest stworzyć pętlę, która się… zapętli. To znaczy – będzie się wykonywać w nieskończoność, bo nigdy nie wystąpi warunek jej zakończenia. Wtedy nasz program się zawiesi, a my otrzymamy komunikat o błędzie, najprawdopodobniej “Stack overflow”.

Ciekawostka: Stack Overflow to bardzo popularny wśród programistów portal z poradami. Więcej na jego temat znajdziesz w Dodatkach.

Ćwiczenia

Jakiego rodzaju pętli użyłbyś w tych przypadkach? Spróbuj je zaprojektować (na kartce).

  • Obieranie ziemniaków
  • Jazda na rowerze do pracy
  • Pisanie książki
  • Wyświetlenie liczb od 1 do 100

 

Zrób to

Ile razy w życiu myłeś ręce? Albo parzyłeś herbatę? Niezliczoną ilość razy. Są to czynności codzienne, nad wykonywaniem których w zasadzie się nie zastanawiamy. Dzieją się po części automatycznie. Trochę tak, jakbyśmy podchodząc do umywalki “włączali program mycia rąk”, a ciało samo wie, co ma robić. Robiło to tysiące razy. Ma gdzieś w mózgu zapisany schemat działania, i tylko go uaktywnia. W zasadzie my go uaktywniamy, myśląc “umyj ręce”.

Umyj ręce:

Podejdź do umywalki

Odkręć wodę

Użyj mydła

Spłucz

Zakręć wodę

Wytrzyj ręce

Podobnych zautomatyzowanych czynności wykonujemy na pewno dużo więcej. To po prostu zestaw prostych instrukcji pod jakąś wspólną nazwą.

Słyszałeś kiedyś o procedurach bezpieczeństwa? To są znowu kolejne kroki do wykonania w przypadku zaistnienia jakiejś sytuacji (“W razie pożaru zrób to i to”). Już samo słowo “procedura” przywodzi na myśl zestaw kolejnych kroków, które należy wykonać w celu osiągnięcia pewnego celu dla danej sytuacji (np procedura sprawdzania systemów pokładowych przed startem samolotu).

Wróćmy do parzenia herbaty.

Zaparz herbatę:

Wstaw wodę

Weź kubek

Nasyp herbaty

Zalej wrzątkiem

Proste, prawda? Nie ma się nad czym zastanawiać. W programowaniu bardzo często posługujemy się podobnymi konstrukcjami. “Zamykamy” pewien zestaw instrukcji jako nazwaną całość. Dzięki temu później, zamiast pisać kilka lub kilkanaście (albo i kilkadziesiąt) razy to samo (wstaw wodę, weź kubek…), po prostu wołamy Zaparz Herbatę, i komputer sam sobie odszuka odpowiednie instrukcje. Takie rozwiązanie ma jeszcze jedną, ogromną zaletę. Instrukcje są spisane w jednym tylko miejscu. Jeśli więc chcemy coś poprawić albo zmienić, to robimy to raz. Jeśli w każdym miejscu, zamiast wołać o herbatę, mielibyśmy wpisany ciąg instrukcji, to musielibyśmy wprowadzać poprawki w każdym z osobna. Łatwo w tej sytuacji o błąd.

Przyjrzyjmy się jeszcze raz naszej metodzie parzenia herbaty. Przedstawionym sposobem można zrobić wiele różnych herbat. Wszystko zależy od tego, po jaką sięgniemy. Możemy to zapisać tak:

Zaparz herbatę (Wybrana herbata):

Wstaw wodę

Weź kubek

Nasyp Wybranej herbaty

Zalej wrzątkiem

Przeważnie robimy sobie Earl Greya, ale jeśli najdzie nas ochota na herbatkę owocową, to wystarczy podmienić Wybraną herbatę na inną, i też otrzymamy to co chcieliśmy.

A teraz wyobraź sobie automat z napojami. Jaka jest jego funkcja? Ma przyjąć pieniądze i wydać upragnioną, chłodną colę. Jak działa w środku? Nie wiesz, i tak naprawdę nie potrzebujesz tego wiedzieć. Dla ciebie ważna jest informacja “wrzuć 2zł to dostaniesz napój”.

To, co właśnie omawiam, w programowaniu nosi nazwę funkcji, procedury lub metody (zależnie od języka). To nic innego jak właśnie zestaw poleceń, które mają wykonać pewną określoną rzecz. Czasami muszą tą rzecz wykonać w oparciu o pewne dane (przetworzyć je w jakiś sposób). Dostarczenie danych do funkcji/procedury/metody odbywa się poprzez parametry. Funkcja, jeśli przetwarza dane, to często zwraca wynik, z którym możemy dalej coś zrobić. Tak jak w kalkulatorze – wpisujemy 2+2, otrzymujemy 4, i z tym 4 możemy dalej coś zrobić.

W przykładzie z herbatą, to rodzaj herbaty jest właśnie tym dostarczanym z zewnątrz parametrem, elementem zmiennym. 

W przykładzie z automatem, parametrem będzie wrzucona moneta, a elementem zwrotnym – napój. 

Prosty przykład funkcji/procedury/metody (przyjmuje dwa parametry, przetwarza je, i zwraca wynik swoich działań):

Dodaj Liczby (Pierwsza liczba, Druga liczba):

Wynik = Pierwsza liczba + Druga liczba

Zwróć Wynik

 

Ćwiczenia

Zaprojektuj (na kartce) kilka prostych funkcji. Zastanów się, czy powinny mieć parametr (jeśli tak, to co nim będzie?) i czy powinny coś zwracać jako wynik swoich działań (jeśli tak, to co?). Posłuż się poniższym schematem:

Wymień PLN na USD (ile PLN chcę wymienić?)

Sprawdź aktualny kurs USD

Przelicz PLN na USD

Zwróć USD

A teraz Twoja kolej:

  • Automat z batonami (taki, który ma kilka rodzajów batonów). Co będzie, jeśli ktoś wrzuci za mało pieniędzy?
  • Funkcja, która zamienia w tekście wszystkie małe litery na duże

 

Czym jestem?

Książka.

Gdy czytasz to słowo, w głowie pojawia ci się obraz. Być może jakiejś szczególnej książki, a być może książki “ogólnie”. Zastanówmy się, jak można scharakteryzować książkę. Chcemy stworzyć ideę książki, jej schemat. Co ma każda książka? Tytuł, autora i pewną liczbę stron. Bez względu na to, czy jest to reportaż, fantastyka czy książka kucharska. Na początek tyle nam wystarczy. Mamy pewną rzecz i jej główne cechy.

Programowanie to próba odzwierciedlenia rzeczywistości za pomocą abstrakcji. Spróbujmy więc stworzyć abstrakcyjny byt, który opisywałby książkę.

Książka

Tytuł

Autor

Liczba stron

W ten sposób możemy opisać książkę. Nie jest to żadna konkretna książka, bardziej schemat książki. Idea, czym książka powinna być.

I na podstawie tej idei możemy stworzyć nieskończenie wiele konkretnych książek.

Inny przykład.

Wyobraź sobie architekta. Siedzi przy wielkim stole, za ucho ma zatknięty ołówek, a przed nim  leży wielki arkusz papieru, na którym widać projekt domu. Gdy skończy pracę, na podstawie jego projektu będzie można zbudować rzeczywisty dom.

Ile domów można zbudować na podstawie jednego projektu? Dowolną ilość.

Czy wszystkie będą identyczne? Nie – będą się różniły szczegółami (każdy właściciel urządzi swój dom inaczej), ale ogólną budowę będą miały taką samą.

Jeszcze jeden przykład.

Na stronach urzędów są do pobrania różne druki, na przykład wniosek o rejestrację samochodu. Możesz sobie taki druk pobrać i wydrukować. A następnie wypełnić swoimi danymi.

Właśnie opowiedziałam ci o klasach i obiektach. Klasa jest takim właśnie schematem, projektem. Obiekt to “urzeczywistnienie projektu”. 

Formularz do pobrania czy projekt domu to klasa. 

Wydrukowany formularz i wybudowany dom to urzeczywistnienie projektu (nazywany obiektem lub instancją klasy).

Wypełniony formularz i urządzony dom to gotowe obiekty, wypełnione indywidualnymi danymi.

W programowaniu bardzo często będziemy tworzyć nasze własne schematy pewnych rzeczy lub idei, które później będziemy wykorzystywać.

Dane do wypełniania obiektów najczęściej będą pochodzić z dwóch źródeł: od użytkownika (np. poprzez wypełnienie jakiegoś formularza) albo z bazy danych (to tam gromadzimy wszystkie zebrane dane).  

A teraz przełóżmy to na programowanie.

Jak zbudować konkretny obiekt na podstawie schematu?

Musimy zadeklarować stworzenie nowego obiektu, który będzie książką. A potem wypełnić ten obiekt konkretnymi danymi.

Stwórz nową Książkę o nazwie MojaKsiążka

MojaKsiążka to konkretny obiekt zbudowany na podstawie schematu Książka. Posiada “w sobie” trzy zmienne: Tytuł, Autor i Liczba stron. Jak się do tych zmiennych dostać, by móc nadać im konkretne wartości? Kropką. (Przeważnie.)

MojaKsiążka.Autor = “Frank Herbert”

MojaKsiążka.Tytuł = “Diuna”

MojaKsiążka.LiczbaStron = 500

 

Działajmy

Czasami opis obiektu tylko za pomocą suchych danych jest niewystarczający. W końcu przedmioty w rzeczywistości mogą wykonywać różne działania. Tak samo klasa może zawierać w sobie funkcje (lub procedury, lub metody; opisane w rozdziale “Zrób to”), a więc zdefiniowane działania.

Jak opisalibyśmy samochód? Ma on cechy, takie jak marka, model, pojemność silnika, wymiary itp. Ale może również wykonywać pewne akcje – uruchom silnik, włącz wycieraczki, wrzuć bieg, otwórz okno.

Jak opisalibyśmy kota?

Cechy: wiek, waga, rasa

Akcje: Jedz, śpij, baw się, drap

A co z lampą?

Cechy: Model, wysokość, rodzaj żarówki, czy jest włączona

Akcje: Włącz/wyłącz, wymień żarówkę

A teraz spróbujmy opisać głośnik, tylko w bardziej złożony sposób.

Głośnik:

Marka

Model

Czy włączony

Aktualna głośność

Głośniej

Jeśli (Czy włączony? = tak)

Aktualna głośność + 1

Ciszej

Jeśli (Czy włączony? = tak)

Aktualna głośność – 1

Mamy więc całkiem sensowny opis głośnika. Wiemy, jakie ma cechy i jakie może wykonywać działania.

 

Ćwiczenia

Spróbuj stworzyć schematy dla poniższych rzeczy. Jakie mają cechy? Jakie mogą wykonywać działania?

  • ołówek
  • talerz
  • komoda z czterema szufladami
  • rower

 

A teraz wymieszaj

Programowanie jest jak budowanie z klocków Lego. Mając kilka elementów, możemy łączyć je na przeróżne sposoby. Właśnie poznałeś kilka klocków. Zobaczmy, co możemy z nich złożyć…

Spróbuj zaprojektować – na kartce – kilka bardziej złożonych “programów”:

  • kalkulator z podstawowymi działaniami (+, -, *, /), który wykonuje działania na podanych mu dwóch liczbach
  • dowolny prosty program według Twojego pomysłu!

 

Co dalej?

Masz już zaszczepione w głowie pewne idee. Teraz nadszedł czas na prawdziwą naukę.

Pozostaje ci wybrać język i rozpocząć swoją przygodę.

Jaki język wybrać?

Jeśli miałabym ci coś polecić, to byłby to dwie opcje:

  • Python, ze względu na przyjazną “gramatykę” i przejrzystość kodu – pisanie w nim w dużej mierze przypomina zwykłe pisanie po angielsku; jest to też język wykorzystywany na uczelniach.
  • Jeden z języków kompilowanych (np Java albo C#). Język kompilowany oznacza, że zanim program się uruchomi, kompilator sprawdza, czy nie ma w nim błędów, i jeśli są, to ci je zakomunikuje. W ten sposób od razu widzisz, gdy coś jest nie tak.

NAJWAŻNIEJSZE: nie poświęcaj zbyt dużo czasu na wybór konkretnego języka. Po prostu weź jakiś i zacznij działać. Najwyżej potem go zmienisz, to już nie będzie duży problem.

 

Dodatki

Poniżej przedstawiam trochę dodatkowych rzeczy – jestem pewna, że okażą się przydatne!

 

Język kompilowany vs język skryptowy (interpretowany)

Współczesne popularne języki możemy podzielić na dwie grupy* – są to tak zwane języki skryptowe i kompilowane. Różnica między nimi polega na tym, że pisząc program w języku kompilowanym, kompilator musi przetworzyć nasz kod do postaci kodu rozumianego przez maszynę, i dopiero wtedy może go uruchomić. Podczas kompilacji następuje też sprawdzenie, czy program nie zawiera błędów – jeśli zawiera, to kompilacja nie powiedzie się. Takie języki dobrze nadają się do pisania większych, bardziej złożonych programów. Łatwiej jest je skalować i rozwijać, a także optymalizować.

Języki skryptowe natomiast działają „na bieżąco” – nie ma żadnego procesu kompilacji; kod jest wczytywany z pliku, poddawany interpretacji (czyli tłumaczony na język komputera), po czym się wykonuje. Jeśli w naszym kodzie są błędy, to dowiemy się o tym dopiero w czasie działania programu. Takie języki dobrze sprawdzają się przy mniejszych aplikacjach.

Języki kompilowane to np C# i Java.

Języki skryptowe to np JavaScript, Python, PHP.

Uwaga: Java z JavaScriptem ma tyle samo wspólnego co koń z koniakiem i rum z rumakiem. Nie myl tych języków, bo to dwa bardzo różne światy!

Uwaga 2: HTML i CSS nie są językami programowania.

Język interpretowany wg Wikipedii

Język kompilowany wg Wikipedii

*podziałów języków programowania jest oczywiście o wiele więcej, ale to nie czas ani miejsce, by to roztrząsać.

 

Materiały do nauki

Youtube – Free Code Camp. Zbiór dobrych kursów z różnych języków:

https://www.youtube.com/channel/UC8butISFwT-Wl7EV0hUK0BQ

Udemy – tu można znaleźć naprawdę dobre kursy za niewielkie pieniądze (bardzo często są promocje i kursy kosztują ok 35-40zł):

https://www.udemy.com/

W3Schools – tutoriale z interaktywnymi przykładami:

https://www.w3schools.com/

Tutorials Teacher – sporo tutoriali z różnych języków:

https://www.tutorialsteacher.com/

Oprócz tego – książki. Niektórzy mogą mówić, że książka jest już nieaktualna w momencie druku. Tak, to prawda. ALE – czy musisz być aż tak na bieżąco? Trzon języka nie zmienia się przez wiele lat. Podczas nauki musisz przede wszystkim opanować język jako taki, a wszystkie nowinki są raczej jego szlifem niż czymś co musisz wiedzieć już teraz natychmiast. Książki mają też kilka zalet: wiedza w nich zawarta jest komplementarna, i powiedzmy sobie szczerze – film na youtuba czy wpis na bloga może zrobić każdy, ale wydać książkę już nie tak łatwo. Jasne, są lepsze i gorsze, ale wystarczy poczytać opinie i wybrać coś dla siebie.

 

Jak rozwiązywać problemy w programowaniu?

Rozbijanie dużych problemów na małe kroki

Masz pomysł na program, ale nie masz pojęcia, jak się za niego zabrać? Nie wiesz, od czego zacząć? To na początku normalne.

Chyba najlepszą opcją będzie rozpoczęcie od wypisania sobie kolejnych kroków w postaci komentarzy. Musimy sobie wyobrazić, co nasz program ma robić i w jakiej kolejności. Potem wypisać to w punktach, a następnie pod każdym takim punktem napisać kod, który to zadanie zrealizuje. Dzięki temu dzielimy jeden duży problem na kilka małych, z którymi powinniśmy sobie bez problemu poradzić.

Kilka przykładów prostych programów, rozbitych na mniejsze kroki i opisanych znajdziesz tu:

https://napograniczu.net/pomysl-na-projekt/

Metoda prób i błędów

Czasem – szczególnie podczas nauki – warto zastosować metodę prób i błędów. Zmień coś w kodzie i zobacz, co się stanie. Może problem zniknie, może pojawi się inny. Spróbuj też to samo zrobić w inny sposób. Możliwości jest naprawdę dużo. Nie bój się eksperymentować, przecież nic nie wybuchnie.

Błędy techniczne i logiczne

Błędy podczas programowania są na porządku dziennym. Czasem czegoś nie przewidzimy, czasem coś działa inaczej niż się spodziewaliśmy, czasem zrobimy literówkę. Gdy wystąpi błąd, należy uważnie przeczytać jego treść, bo jest bardzo duża szansa, że jest tam zawarte sedno problemu. Drugą rzeczą jest zidentyfikowanie problematycznego miejsca w kodzie – tu znowu z pomocą przychodzi komunikat błędu, który powinien podpowiedzieć, w której linii kodu nastąpił wyjątek (po to właśnie są one numerowane).

Jeśli jednak komunikat błędu niewiele nam mówi, warto wpisać jego treść w Google – dostaniemy szereg stron z wyjaśnieniem, co on oznacza i gdzie należy szukać przyczyny problemu. Jeśli wśród wyników wyszukiwania zobaczysz stronę StackOverflow – klikaj śmiało. To świetny serwis, który zawiera ogromny zasób wiedzy. Znajdziesz tam 99% odpowiedzi na swój problem. I czasem podane tam przykłady i wyjaśnienia są lepiej przyswajalne niż te z oficjalnej dokumentacji języka.

O ile błędy techniczne są jeszcze stosunkowo proste do wyłapania i załatania, o tyle większym problemem mogą być błędy logiczne. Czyli program działa, ale nie otrzymujemy oczekiwanego wyniku tych działań. To są popełnione przez nas przeoczenia albo błędy w algorytmach, które trudno zidentyfikować. Trzeba analizować kod krok po kroku. Tu przydatne może się okazać debugowanie.

Debugowanie

Debugować można na różne sposoby. Możemy wydzielić kawałek kodu, który chcemy sprawdzić, i nanosić w nim drobne zmiany w celu sprawdzenia, czy i jak zmieniło się jego działanie. Możemy wypisywać wartości kluczowych zmiennych po każdym wykonanym przez program kroku, by monitorować zachodzące zmiany. Możemy też skorzystać z dostępnych dla danego języka debuggerów, dzięki którym prześledzimy działanie całego kodu linia po linii, podczas wykonywania się programu. Możliwości jest sporo, i warto poznać je wszystkie. Jeśli nie masz pewności, jak to robić w wybranym przez ciebie języku, zapytaj wujka Googla – na pewno zna odpowiedź.

Już coś umiem. Chcę się sprawdzić. Jaki program mam napisać?

Tego typu pytania to kolejny częsty temat poruszany przez początkujących. Dlatego przejrzałam nieco stron z pomysłami na programy i zrobiłam poniższe zestawienie:https://napograniczu.net/pomysl-na-projekt/

Innym pomysłem na sprawdzenie się „w boju” jest rozwiązywanie zadań. Listę popularnych stron z takimi zadaniami znajdziesz tu:https://napograniczu.net/programistyczne-wyzwania-gdzie-ich-szukac/

 

Alexandretta

Kobieta gracz. Po godzinach pracy, z zapałem i piekielnymi ognikami w oczach biega po lochach i tłucze bogu ducha winne zombie czy inne szkielety. Miłośniczka wszelkiej maści cRPG. Notoryczne problemy z wyborem klasy i rasy, bo wszystko fajne... W chwilach zwątpienia zatraca się przy dźwiękach mieczy, roztrzaskiwanych tarcz i okrzyków bojowych, słuchając wiking metalu z zimnej, niegościnnej, odległej Skandynawii. Czasem zdarzy jej się pograć w jakąś strategię bądź nawet w FPP, ale tylko na easy, żeby nie psuć sobie niepotrzebnie nerwów.

Related Articles

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

Witryna wykorzystuje Akismet, aby ograniczyć spam. Dowiedz się więcej jak przetwarzane są dane komentarzy.

Back to top button