D/Pierwszy program
Pierwszy program
[edytuj]Nikt nie wymyślił lepszego sposobu nauki programowania niż po prostu zaczęcie programowania. W tym rozdziale zaznajomisz się z najważniejszymi ideami programowania oraz minimalną wiedzą o składni języka D. Zanim przejdziesz do następnych rozdziałów upewnij się że wszystko rozumiesz oraz udało Ci się uruchomić prezentowane (najlepiej ręcznie przepisane!) programy z pozytywnym skutkiem.
Nasz pierwszy program (tradycja wszystkich kursów programowania) będzie po prostu programem który zaraz po uruchomieniu wypisuje tekst Witaj, świecie! (czy też bardziej tradycyjnie Hello world), po czym kończy.
Otwórz swój ulubiony edytor tekstowy i wpisz:
import std.stdio;
// Nasz pierwszy program komputerowy w D
void main() {
writefln("Witaj, świecie!");
}
Upewnij się, iż wpisałeś wszystkie znaki dokładnie jak to jest przedstawione powyżej. Komputer jest bardzo czuły na wszelkie błędy i nie zrozumie Ciebie jeśli nie napiszesz co ma zrobić dokładnie w takiej postaci jaką jest w stanie przetwarzać. A więc wszystkie kropki, cudzysłowy, nawiasy, średniki są ważne!
Zauważ, że w treści programu użyliśmy polskich liter - jeśli Twój edytor nie potrafi obsługiwać kodowania UTF-8, wpisz tam na przykład znane "Hello world" (pamiętaj aby cudzysłowy były analogicznie jak w powyższym przykładzie).
Zapisz ten 5-linijkowy kod do pliku hello.d w katalogu ~/kurs_d/
Pora teraz na kompilację i uruchomienie.
Kompilacja i uruchomienie
[edytuj]I tu pojawia się już pewna różnica w zależności jakiego systemu operacyjnego używasz. Dlatego upewnij się iż przeczytałeś rozdział Używanie kompilatora. Tu powtórzymy to na naszym konkretnym programie.
W przypadku systemu Linux, uruchom terminal (o ile już w nim nie pracujesz lub skorzystaj z konsoli) i zakładając, iż plik hello.d zapisałeś w katalogu kurs_d w katalogu domowym, wydaj polecenie
~$ cd kurs_d ~/kurs_d$ dmd hello.d
(znak $ oznacza jedynie znak zachęty powłoki, jego nie wpisuj)
Powinniśmy otrzymać taki rezultat:
~/kurs_d$ dmd hello.d gcc hello.o -o hello -m32 -lphobos -lpthread -lm ~/kurs_d$
co oznacza, że kompilator dmd skompilował nasz program do pliku obiektowego hello.o, a potem skonsolidował go do postaci wykonywalnej wraz z bibliotekami przy pomocy kompilatora gcc do pliku hello. Sprawdźmy czy rzeczywiście mamy tam te pliki:
~/kurs_d$ ls hello hello.d hello.o
Jak widać tak, uruchommy plik wykonywalny (ang. executable) wpisując ./hello i sprawdźmy czy działa zgodnie z oczekiwaniami:
~/kurs_d$ ./hello Witaj, świecie! ~/kurs_d$
Nasz pierwszy program zadziałał (jeśli wybrałeś wersje bez polskich liter, albo wpisałeś własny tekst powinieneś otrzymać właśnie ten tekst).
Ewentualne błędy
[edytuj]Przestarzały przykład, writeln jest już dostępne w „nowszych” wersjach biblioteki standardowej!
Może się zdarzyć, iż mimo najlepszych chęci coś pominąłeś przy przepisywaniu kodu do edytora. Podczas próby kompilacji, kompilator zapewne wypisze wtedy wiele informacji o błędach i nie wygeneruje pliku obiektowego, a tym bardziej wykonywalnego. Nie martw się. Błędy generowane przez kompilator są bardzo pomocne, i musisz się do nich przyzwyczaić - czym większe programy będziemy pisać tym większe prawdopodobieństwo, iż gdzieś nam wpadnie jakaś literówka, nie zamkniemy jakiegoś nawiasu czy cudzysłowie. Radą na to jest uważne przeanalizowanie komunikatu o błędzie wygenerowanym przez kompilator. Kompilator poda przybliżoną linię w której wystąpił błąd i dlaczego uznał to za błąd.
Pokażemy na przykładzie jak się zachowa program z niepoprawnym kodem.
import std.stdio;
// Nasz pierwszy program komputerowy w D
void main() {
writeln("Witaj, świecie!");
}
Kod jest ten identyczny jak poprzednio, z jednym mały wyjątkiem: zapomnieliśmy (np. przez zbyt szybkie pisanie) o literze f w writefln, uzyskując jedynie writeln, zapiszmy to jako hello2.d i spróbujmy skompilować:
~/kurs_d$ $ dmd hello2.d hello2.d(4): Error: undefined identifier writeln hello2.d(4): Error: function expected before (), not writeln of type int ~/kurs_d$
I rzeczywiście kompilator wygenerował. Nie zna on identyfikatora writeln (ponieważ powinniśmy tam wpisać writefln), napisał również iż jest to w pliku hello2.d w linii numer 4. Wystarczy więc z powrotem otworzyć edytor i sprawdzić dokładnie co wpisaliśmy. Po poprawce wszystko pójdzie już dobrze. Skąd natomiast druga linia o błędzie (też w linii 4)? Nie trzeba się nią przejmować (ma ona swoje powody), często jest tak iż jeden błąd w kodzie pociąga za sobą wiele błędów kompilacji, zwykle więc poprawiamy błędy patrząc na najwcześniejsze błędy.
Ponieważ programujesz zapewne po raz pierwszy w D, a być może po raz pierwszy w życiu, możesz mieć więcej błędów w kodzie. Analizuj błędy kompilatora po kolei aż wszystkie pomyłki poprawisz i uruchomisz swój program.
Opis programu
[edytuj]Jak na tak prosty program jest on całkiem duży, aż 5 linijek. Jest tak dlatego iż praktycznie wszystkie oprócz jednej linijki (tej z writefln("Witaj, świecie!"); ) są w pewnym sensie obowiązkowe.
Przeanalizujmy je po kolei:
import std.stdio;
Ta linia mówi kompilatorowi aby "zaimportować" "moduł" o nazwie "std.stdio". Co to oznacza? Moduły są zbiorami pewnych funkcji o podobnym przeznaczeniu, np. moduł std.stdio, jest dostarczanym razem z kompilatorem, modułem odpowiedzialnym za wypisywanie na ekran (poprawnej standardowe wyjście) różnych danych (napisów, liczb). Musimy powiedzieć kompilatorowi o tym module ponieważ w treści naszego programu chcemy użyć funkcji writefln która służy właśnie do wypisywania na ekran - funkcja ta znajduje się w tym module (na marginesie jej pełna nazwa to std.stdio.writefln, co wskazuje gdzie się dokładnie znajduje).
Bez tej linijki kompilator nie znałby funkcji writefln, podobnie jak nie zna funkcji writeln przedstawionej poprzednio przy przykładzie programu z błędem.
Przy tak podanym imporcie, kompilator znajduje pliki tego modułu (możemy go traktować jako bibliotekę funkcji, czy jak plik nagłówkowy dla osób znających C), analizuje je i sprawdza jakie są w niej funkcji. Wśród wielu dostępnych funkcji jest tam właśnie funkcja writefln. Ponieważ będziemy chcieli w następnych przykładach wyświetlać wyniki naszych programów, linijkę tą będziesz spotykał praktycznie w każdym programie.
Przejdźmy do następnej linijki
// Nasz pierwszy program komputerowy w D
Ta linijka stanowczo nie wygląda na program komputerowy. Jest to komentarz, i jego treść nie ma wpływu na interpretację czy wykonanie programu. Został on tu dodany aby opisać czym jest nasz program. W D istnieje kilka rodzajów komentarzy - tutaj użyliśmy komentarza który rozpoczyna się od dwóch znaków // i rozciąga się, aż do końca linijki. Dlaczego wpisaliśmy komentarz w drugiej linijce a nie w pierwszej? Nie ma to znaczenie, jest to pewne konwencja. Do tej pory jedynie wykonaliśmy import modułu, i nie jest to jeszcze jakikolwiek program, nasz program zaczyna się właśnie w następnej linijce:
void main() {
Pełno różnych nawiasów i nieznanych słów. To tutaj zaczyna się wykonywanie naszego programu. main jest nazwą funkcji (do funkcji przyjdziemy później) od której zaczyna się uruchomienie programu (poza pewnymi wyjątkami). void przed nazwą funkcji iż funkcja ta nie zwraca żadnych rezultatów (void z ang. pustka), oraz nie przyjmuje żadnych argumentów (nawias otwierający i zamykający: (), bez niczego pomiędzy ). Z koleji nawias klamrowy (wąsaty, {) otwiera "treść" naszej funkcji - ma on swoja parę w postaci nawiasu zamykającego (}) w ostatniej linii programu. Wszystko co jest pomiędzy tymi nawiasami jest treścią funkcji. W naszym przypadku treść funkcji składa się z jednego polecenia:
writefln("Witaj, świecie!");
Polecenie to jest wywołaniem funkcji writefln (pochodzi ona z modułu std.stdio, i kompilator już ją zna, może nawet sprawdzić czy używamy jej poprawnie), z argumentem "Witaj, świecie!", który przekazujemy jej w nawiasach okrągłych. Jest to analogiczny zapis jak w matematyce: Jeśli mamy funkcję f, to aby ją "uruchomić", tzn. obliczyć ją, należy przekazać jakieś argumenty w nawiasie. Na przykład: f(5). Z tym że zamiast liczby w naszym przypadku użyliśmy napisu "Witaj, świecie!".
Zauważ, iż po zamykającym nawiasie umieściliśmy średnik. Średnik ten musimy wypisać po każdym wywołaniu funkcji (oraz w kilku innych przypadkach) i oddziela on kolejne polecenia.
}
Zamykamy nawias klamrowy funkcji main.
W chwili wykonania skompilowanego programu wykonanie rozpoczyna się od funkcji main, i po kolei są wykonywane instrukcję, aż do zamykającego nawiasu klamrowego (sparowanego z otwierającym nawiasem przy void main()), albo do wyjścia w inny sposób.
Zaczynamy komplikować
[edytuj]To był najprostszy możliwy przykład. Jednak nie jest to przykład wystarczający do używania języka.
Dopiszmy więcej instrukcji w naszym programie i zróbmy to trochę nieschludnie:
import std.stdio;
// Nasz drugi program komputerowy w D
void main() {
writefln("Witaj, świecie!");
writefln("Jestem programem w D.");
writefln();
writefln( // ciekawe co z tego wyjdzie
"Potrafimy już coraz więcej."
)
;
}
Zapewne zauważysz, iż 5 pierwszych linijki jest praktycznie identycznych z poprzednim programem (zmieniliśmy jedynie komentarz i dodaliśmy jedną pustą linijkę aby oddzielić wizualnie import od naszego programu). Ciekawe rzeczy zaczynają się dalej. Dopisaliśmy kolejną linijkę która drukuje napis "Jestem programem w D", i jest bardzo podobna do tej w "Witaj, świecie!". Jak widzisz teraz średnik na końcu poprzedniej linijki się przydaje, średnik jest separatorem pomiędzy tymi dwoma poleceniami.
Dalej wykonujemy funkcję writefln bez argumentów (lecz nadal wpisując nawiasy), polecenie to wypisuje pustą linię - chcemy oddzielić następny wypisywany tekst od tego co już wyświetliliśmy.
Następne 3 linijki wbrew pozorom to pojedyncze wywołanie funkcji writefln analogiczna do dwóch pierwszych. To iż polecenie tym razem zajmuje więcej miejsca nie ma znaczenia - D jest nieczuły (do pewnego stopnia) na spacje i znaki nowej linii w kodzie źródłowym. Na podstawie identyfikatorów i nawiasów potrafi nadal to poprawnie zrozumieć. Tą swobodę w układaniu instrukcji można wykorzystać, aby program był bardziej czytelny dla człowieka, na przykład poprzez tworzenie bloków instrukcji, czy oddzielanie (grupowanie) instrukcji od siebie pustymi liniami.
Jak widzisz również wpisaliśmy dodatkowy komentarz. Zgodnie z oczekiwaniami zostanie on zignorowany (aż do końca linijki), i równie dobrze mogło by go nie być.
Podsumowując informację o niewrażliwości na białe znaki:
writefln("Witaj, świecie!");
writefln ( "Witaj, świecie!" );
writefln(
"Witaj, świecie!"
);
writefln ( "Witaj, świecie!" ) ;
oznaczają dokładnie to samo.
Z kolei następująca linia będzie oznaczała co innego
writefln ( "Wi taj, świ ecie!" ) ;
ponieważ dodaliśmy spacje wewnątrz napisu, co spowoduje oczywiście wyświetlenie innego napisu, a z kolei
writ efln ( "Witaj, świecie!") ;
nie zadziała ponieważ identyfikatory w języku D nie mogą posiadać białych znaków, i kompilator uzna iż chcemy coś zrobić z identyfikatorem writ - oczywiście zaprotestuje, bo takiego nie zna, tak samo jak efln, a cała konstrukcja wyda mu się bezsensu o czym poinformuje. Sprawdź te przykłady, wpisując je pomiędzy { } w funkcji main, nie zapominając o średnikach.
Zapiszmy nasz kod do pliku (hello3.d), skompilujmy i uruchommy:
~/kurs_d$ dmd hello3.d gcc hello3.o -o hello3 -m32 -lphobos -lpthread -lm ~/kurs_d$ ./hello3 Witaj, świecie! Jestem programem w D. Potrafimy już coraz więcej. ~/kurs_d$
Program zgodnie z opisem: wypisał dodatkowe linie, w tym jedną pustą, a komentarz zignorował.
Więcej o komentarzach
[edytuj]Ponieważ opisywanie programu najlepiej przeprowadzać na kodzie źródłowym, jeszcze lepszym będzie umieszczenie opisu w samym kodzie źródłowy. Dla naszego podręcznika jest to tym wygodniejsze, iż nie trzeba niczego powtarzać dwa razy (w razie czego będziemy numerować linie, i się do nich odwoływać), a dla programisty, czyli Ciebie, jest to sposób na szybsze zrozumienie programu.
Stosowanie komentarzy świadczy o dobrej praktyce programistycznej, pozwalają upewnić się że wszystko jest tak jak chcemy, oraz pozwalają ewentualnym innym osobą na szybsze zaznajomienie się z obcym kodem. Nawet prosty kod warto komentować, bo zapewne po miesiącu Twój własny kod wyda się obcy, i w najgorszym przypadku będzie trzeba napisać go od nowa.
Komentarze w D dzielą się na wiele rodzajów, tu opiszemy dwa.
Pierwszy już poznaliśmy, jest to komentarz liniowy (zwany komentarzem w stylu C++):
// to jest komentarz liniowy
Będziemy używać go do opisu następnej linijki
// wyświetlamy tekst powitalny
writefln("Witaj, świecie!");
albo tego co jest w aktualnej linii:
writefln("Jesteśmy programem w D"); // najlepszym języku na Ziemi
albo
writefln("No pewnie"); // // dodatkowe znaki // i tak będą ignorowane
Jeśli mamy więcej do komentowania, możemy po prostu dodać więcej komentarzy liniowych w oddzielnych liniach:
// wyświetlamy tekst powitalny
// po angielsku powinniśmy tu wstawić:
// "Hello world"
writefln("Witaj, świecie!");
lecz ponieważ może to być męczące, a same komentarze liniowe jako tako nie są związane ze sobą (teoretycznie moglibyśmy je poprzestawiać miejscami, co by było bez sensu), używamy do takich dłuższych komentarzy komentarza blokowego (w stylu C):
/* Wyświetlamy tekst powitalny
po angielsku powinniśmy tu wstawić:
"Hello world"
*/
writefln("Witaj, świecie");
Komentarz tego typu rozpoczynamy od /* (bez spacji pomiędzy), a kończymy */. Wszystko pomiędzy tymi dwoma dwuznakami jest pojedynczym komentarzem i jak każdy komentarz jest ignorowane.
Analizując różne programy spotkasz się zapewne z różnymi formami tego komentarza:
/* Krótki komentarz blokowy */
/* Krótki
komentarz
blokowy */
/* Krótki
* komentarz
* blokowy
*/
czy tez wersja z dwiema gwiazdkami:
/** Wywalanie funkcji writefln
* W tym miejscu wywołujemy funkcję writefln
* celem wyświetlenia napisu "Witaj, świecie!"
* Note: Aby wypisać tekst po angielsku, zmień
* napis na "Hello world"
*/
Jest to tak zwany komentarz dokumentacyjny, i może posłużyć do automatycznego wygenerowania schludnej dokumentacji, z której mogą na przykład korzystać inni programiści. Będziemy używać tego typu komentarzy przy definiowaniu nowych funkcji, czy własnych typów - dając tym samym możliwość innym (lub nam samym w późniejszym czasie) bardzo łatwego wykorzystania naszych programów.
Język D udostępnia jeszcze jeden rodzaj komentarzy blokowych, w którym zamiast gwiazdki wykorzystujemy znak '+':
/+ Inny
komentarz
blokowy +/
Ten typ komentarzy dopuszcza zagnieżdżanie:
/+W tym komentarzu umieściliśmy /+drugi komentarz+/ w środku.+/
Ćwiczenia (obowiązkowe)
[edytuj]Wiedząc, że writefln oprócz napisów może przyjmować też liczby i wyrażenia matematyczne spróbuj wyliczyć ile wynosi 2+2 i 6*7 wypisując to w formacie w formacie "2 + 2 = %d\n6 * 7 = %d".