Ucz się programowania, nie języków programowania - część 3

W poprzednim odcinku "Ucz się programowania, nie języków programowania" pisałem o zmiennych. Dziś odpowiemy sobie na pytanie: jakie dane mogą przechowywać zmienne. Języki programowania umożliwiają nam operowanie na liczbach, napisach a nawet danych typu "Osoba".

Czytaj inne artykuły z cyklu:

Dla procesora sprawa zmiennych jest dość prosta.

Przykładowo jednostki z rodziny x86 mają rozkazy pozwalające na zapisanie danych do jednego z rejestrów (jak, już pisaliśmy: wewnętrznych komórek pamięci) mogących przechowywać 32 bity informacji (a więc zbioru 32 komórek mogących przechowywać "0" lub "1").

W tym, najpopularniejszym obecnie typie procesora dla komputerów biurkowych obok rozkazów zapisu do 32-bitowego rejestru występują polecenia pozwalające na umieszczenie danych w jego 16-bitowej części lub 8-bitowej części tej części. W procesorach 64-bitowych typu x86_64 każdy z 32-bitowych rejestrów jest połową większego, 64-bitowego. Sumując: procesor x86 zna trzy "typy zmiennych" a x86_64 - cztery. Danymi, które można w nich zapisać są oczywiście liczby.

Jednak nawet programiści piszący w assemblerze, czyli języku, w którym jedno polecenie przekłada się na jeden rozkaz procesora, cenią sobie fakt, że napisy wyglądają w kodzie źródłowym "normalnie". Także dla największych geeków "Hello world" prezentuje się bardziej przyjaźnie niż zapisane w systemie szesnastkowym kody ASCII - w tej postaci "Hello world" to 48656C6C6F20776F726C64.

Podejrzane typy

Programiści mówią, iż "assembler jest językiem dynamicznym o słabej kontroli typów". "Dynamiczny" brzmi zachęcająco, lecz co oznacza dla ludzi piszących aplikacje? "Dynamiczność" bądź "statyczność" języka to stosunek kompilatora do zmiennych i ich typów. Język jest dynamiczny jeżeli ta sama zmienna może w ciągu działania programu wskazywać na wartości różnych typów - np. najpierw na liczbę a później na napis. "Język procesora" - assembler jest dynamiczny, gdyż dynamiczna jest pamięć komputera. Możemy zapisać stan konta, a następnie adres cioci w tych samych komórkach.

W przeciwieństwie do kwestii "dynamiczności" i "statyczności" sprawa ścisłej lub słabej kontroli typów nie jest kwestią prostą. Słaba kontrola typów występuje, gdy za pomocą prostej operacji możemy np. skonwertować liczbę na napis, bądź też wartość liczbową do napisu dołączyć.

Jak pisze Bruce A. Tate w książce "Więcej niż Java": "Język o ścisłej kontroli typów wymusza stosowanie w każdej operacji typów, które są ze sobą zgodne".

Trudno jednak poradzić sobie bez np. przekształcania liczby w napis, więc wskazanie języka o "w pełni ścisłej" kontroli typów byłoby trudne.

"Siłę" kontroli typów można więc stopniować w zależności od tego, na ile wykonywana operacja wymaga bezpośredniego zaznaczenia przez programistę, że zamienia jeden typ w drugi. Zwłaszcza, że zamiany takie mogą mieć różną postać. Możemy chcieć by przekształcenia liczby 97 w napis.

Operacja taka da nam oczywiście "97", ale jeśli będzie to rzeczywiście "przekształcenie". Jeżeli pomylimy je z "potraktowaniem" tej liczby jako napisu naszym oczom ukaże się litera 'a', gdyż 97 to jej kod w tablicy ASCII, która wykorzystywana jest do tłumaczenia liczb na znaki i vice-versa.

Tak się składa...

Typy w językach programowania to jednak nie tylko znaki i liczby. Jeżeli nasz program ma przechowywać informacje o pracownikach, to czyż nie byłoby świetnie gdyby występował w nim typ danych "pracownik"?

Już prostsze języki takie jak Pascal bez rozszerzeń obiektowych czy C dają taką możliwość. Pozwalają one na definiowanie typów złożonych, o czym pisałem już w poprzednim odcinku.

Podstawowy typ złożony w Pascalu to rekord, w C - struktura. Jeśli nasz nowy typ opisuje pracownika, to może on składać się z napisu "imię", napisu "nazwisko", liczby "płaca" i adresu. Warto, aby adres był także typem złożonym przechowującym napis "miasto", napis "ulica", napis "numer domu" i liczbę "numer lokalu". Numer domu zapiszemy jako napis, gdyż może on zawierać litery, np. 37A. Ostatni aspekt naszej konstrukcji uświadamia, że wybór typów w programowaniu nie jest sprawą trywialną. Warto pamiętać o maksymie Einsteina - "Wszystko powinno być tak proste jak to tylko możliwe, ale nie prostsze".

Obecnie dominującym podejściem jest tzw. programowanie obiektowe, w którym typy związane są z czynnościami - np. czynność "jazda" przypisana jest do typu "pojazd". Ponieważ typy "samochód" i "motocykl" są podtypami typu "pojazd", także im można wydać polecenie "jedź".

Swoistym typem może być też wykonywalna część programu. Wynika to wprost z faktu, że architektura komputerów podążyła w kierunku wybranym przez von Neumanna - ta sama pamięć przechowuje dane i rozkazy.

Warto też zauważyć, że nie każdy proces jest zależny od typu obiektów jakie mu podlegają. Kolejka działa tak samo, niezależnie czy jest kolejką samochodów oczekujących na dojazd do dystrybutora, czy ludzi, którzy stoją przy kasie supermarketu. Ten kto stanął pierwszy, pierwszy opuści kolejkę.

Dlatego powstał zbiór rozwiązań pozwalających na niezależny od typu zapis algorytmu. Nosi on nazwę programowania generycznego. Wnikanie w modele programistyczne byłoby jednak z mojej strony pewnym przeskokiem. Skoki zaś nie są w programowaniu rzeczą zalecaną.

To już ostatni tekst z serii "Ucz się programowania, nie języków programowania". Oczywiście jest to temat rzeka i trudno go wyczerpać w kilku artykułach, mam jednak nadzieję, że udało mi się Was zainteresować.


Zobacz również