Z Skrypty dla studentów Ekonofizyki UPGOW
WSTĘP
Programowanie komputera to po prostu tworzenie listy poleceń (instrukcji) do wykonania przez mikroprocesor. Taką listę poleceń – sporządzoną według reguł danego języka programowania – nazywamy programem komputerowym. Z biegiem lat powstało bardzo wiele języków programowania, można je z grubsza podzielić na:
- niskiego poziomu (maszynowe, asemblery). Podzespoły elektroniczne mikroprocesora przetwarzają informacje dostarczane w postaci binarnej (ciągi zer i jedynek). Programowanie w języku maszynowym, czyli na najniższym poziomie symboliki i abstrakcji, polega na dostarczaniu procesorowi rozkazów oraz danych bezpośrednio, w postaci strumienia zer i jedynek. Oto przykład takiego kodu maszynowego:
111010100000000000001111111111111111100010011101100000000010100000010
Stopień wyżej w hierarchii pojawiają się asemblery. Poszczególnym rozkazom maszynowym odpowiadają określone symbole literowe, tzw. mnemoniki, a nie ciągi liczb. Zastąpienie sekwencji zer i jedynek, reprezentujących rozmaite fragmenty rozkazu maszynowego, przez ich symboliczne nazwy czyni program nieco bardziej czytelny dla człowieka. Spójrzmy na fragmentu kodu w asemblerze:
movw %ax, %fs movw %ax, %gs lgdt saved_gdt lidt saved_idt
Każdy język niskiego poziomu jest związany z danym typem procesora, jego listą rozkazów, architekturą, tak pisane programy niesposób przenosić pomiędzy platformami bazującymi na różnych procesorach. Współcześnie asembler jest wykorzystywany do programowania mikrokontrolerów lub tworzenia niewielkich, wysoce specjalistycznych kodów, wymagających bardzo wysokiej efektywności, programów antywirusowych, itp.
- wysokiego poziomu (np. Fortran, Pascal, C, C++, Java, Python, C#, Ruby, …). Oferują one instrukcje składające się z słów angielskich, znaków logicznych i operacji matematycznych, zwykle też bibliotekę funkcji standardowych lub klas.
Spójrzmy na kod napisany w jednym z języków wysokiego poziomu:
int suma = 0; for (int i = 0; i < 6; i = i + 1) suma = suma + i;
Nawet nie znając tego języka, łatwo się domyślić, że jest to sumowanie liczb całkowitych od zera do 5, tj. po wykonaniu dodawania 0 + 1 + 2 + 3 + 4 + 5 wartość zmiennej suma wynosi, suma = 15. Dzięki bardziej czytelnym instrukcjom, kodowanie algorytmów w języku wysokiego poziomu jest łatwiejsze, przejrzystość programu wzrasta, wygodniej wprowadzac modyfikacje i korygować ewentualne błędy. Zwykle jednej instrukcji języka wysokiego poziomu odpowiada od kilku do kilkunastu elementarnych rozkazów asemblera. Pomimo różnic pomiędzy poszczególnymi językami wysokiego poziomu, w każdym z nich występuje niewielka grupa instrukcji podstawowych, służących do sterowania przebiegiem programu. Omówię je, wraz z przykładami, w pierwszym rozdziale skryptu. Oprócz języków niskiego lub wysokiego poziomu pojawiły się zintegrowane pakiety obliczeniowo-programistyczne (np. MAPLE, MATHEMATICA, MATLAB, LabVIEW, ...). Od strony formalnej, to programy komputerowe, napisane w języku wysokiego poziomu. Na przykład kod źródłowy pakietu MATLAB początkowo był pisany w Fortranie, obecnie jego kolejne wersje przygotowuje się za pomocą C/C++. Pakiety te są jednak tak zaawansowane, iż spełniają nie tylko rolę potężnego hiperkalkulatora, ale umożliwiają również programowanie. Oferują one bardzo szeroką paletą instrukcji symbolicznych, ułatwiających intuicyjne wykonywanie zadań, programista bardziej skupia się na tym „co robić ?” (task oriented programing), niż „jak robić ?”. Jedna instrukcja symboliczna pakietu potrafi uruchomić bardzo skomplikowany algorytm (kaskadę algorytmów), który w rodzimym języku wysokiego poziomu (np. C++) zajmuje niekiedy dziesiątki czy nawet setki linii kodu. Dla ilustracji, kilka instrukcji z pakietu/programu MATHEMATICA®:
- chcąc numerycznie obliczyć całkę oznaczoną z funkcji f(x) = sin[sin(x)] w przedziale < 0, 2> piszemy tylko jedną symboliczną instrukcję:
In[1]=NIntegrate[Sin[Sin[x]],{x,0,2}] - w celu znalezienia liczbowych wartości pierwiastków równania piątego stopnia x5 -2 x + 3 = 0 wystarczy wydać polecenie
In[2]=NSolve[x^5 -2 x + 5 = = 0, x] - jak wiadomo, operacja odwracania macierzy jest dość uciążliwa. Tutaj pojedyncza komenda:
In[3]=Inverse[ {1, 2, 3}, {4, 2, 2}, {5, 1, 7} ]
od razu tworzy macierz odwrotną do macierzy wyjściowej
\(\begin{bmatrix} 1 & 2 & 3 \\ 4 & 2 & 2 \\ 5 & 1 & 7\end{bmatrix}\)
- przebieg funkcji f(x) = x3 sin(3 x) cos(x/2) exp(-x) nie jest oczywisty. Po uruchomieniu instrukcji
In[4]=Plot[ x^3 Sin[4 x] Cos[x/2] Exp[-x], {x, 0, 2 Pi}]
wykres f(x) dla zmiennej x leżącej w przedziale < 0, 2π > pojawi się na ekranie komputera
Kolejny przykład, tym razem z pakietu MATLAB®, którym będziemy się posługiwać w dalszej części skryptu. Instrukcja
R = rand(10,10)
tworzy macierz o wymiarach 10 × 10, wypełnioną liczbami losowymi o rozkładzie jednostajnym z przedziału < 0, 1 >. W wielu zagadnieniach technicznych lub ekonomicznych potrzebne są wartości własne macierzy, MATLAB potrafi je obliczyć. Zbiór wartości własnych takiej dość niezwykłej, „przypadkowej” macierzy z naszego przykładu zobaczymy po wykonaniu komendy
eig(R)
Zauważmy, iż nazwy własne symbolicznych instrukcji pakietów są starannie dobrane, klarownie informują, jakie zadanie zostanie wykonane. Wśród pakietów obliczeniowo-programistycznych, na szczególną uwagę osób aktywnych w dziedzinie szeroko rozumianej ekonomii (ubezpieczenia, analizy finansowe i giełdowe, „risk manager”, wycena opcji, przetwarzanie danych, . … itp.) zasługują MATLAB i MATHEMATICA. Obydwa są komercyjne, ale dla MATLAB równolegle istnieje darmowy odpowiednik (klon), program OCTAVE (http://www.gnu.org/software/octave). Między innymi z tego powodu kurs programowania w środowisku zintegrowanym poprowadzę przy użyciu MATLAB.