Wykład pierwszy: Składnia języka occam cz.1
Transkrypt
Wykład pierwszy: Składnia języka occam cz.1
Occam Wykład pierwszy Motto: „Nie należy mnożyć bytów ponad miarę” William of Occam 1 Historia języka Język occam powstał w roku 1983. Jego autorem był David May, pracownik firmy Inmos. Jest to język programowania współbieżnego, pierwotnie przeznaczony dla transputerów. Model współbieżności operaty jest na języku formalnym CSP (ang. Communicating Sequential Processes) opracowanym przez C.A.R Hoare'a. Zakłada on wymianę informacji między procesami wyłącznie za pomocą przekazywania komunikatów (ang. message passing). Obecnie rozwojem tego języka zajmuje się Uniwersytet w Kent. Najnowsza z wersji occama opracowana w tym ośrodku zawiera rozszerzenia zainspirowane tak zwanym rachunkiem pi. W chwili obecnej occam dostępny jest również na inne platformy niż tansputery. 2 Główne cech języka Occam jest językiem służącym do programowania aplikacji współbieżnych (CSP). Charakteryzuje się prostą składnią i łatwością oprogramowywania komunikacji międzyprocesowej, szczególnie nadaje się do celów edukacyjnych związanych z nauczaniem programowania współbieżnego. 3 Podstawowe pojęcia i elementy języka Occam jest językiem rozpoznającym wielkość liter (ang. case sensitive). 1 Proces jest podstawowym pojęciem dotyczącym składni occama. Proces możemy traktować jak „czarną skrzynkę” wykonującą pewne operacje i komunikującą się z pozostałymi procesami poprzez kanały. Procesami prostymi są instrukcje przypisania, instrukcje wysyłania i odbioru przez kanał oraz procesy SKIP i STOP. Occam umożliwia tworzenie grup procesów, które mogą wykonywać się sekwencyjnie lub współbieżnie (równolegle). Instrukcja przypisania zapisywana jest przy pomocy tego samego symbolu co w języku Pascal (:=). Po jej prawej stronie występuje zmienna określonego typu, po jej lewej stronie może występować wartość (literał) lub wyrażenie. Kolejność działań musi być określona przy pomocy nawiasów okrągłych np.: z:= (4*x)+2 Kanał jest specjalnym rodzajem zmiennej, która stanowi połączenie typu punkt – punkt między dwoma procesami współbieżnymi. Procesy te mogą być wykonywane zarówno w obrębie jednego procesora, jak i w obrębie dwóch różnych procesorów. Komunikacja przez kanały jest niebuforowana i zachodzi tylko wtedy, kiedy obydwa procesy są gotowe. Instrukcja odbioru jest oznaczana symbolem pytajnika (?), natomiast operacja nadawania przez kanał jest oznaczana symbolem wykrzyknika (!). Przykład: kan?x kan!x Proces SKIP jest prostym procesem, który rozpoczyna się, nie wykonuje żadnej operacji i kończy się (odpowiednik rozkazu NOP w procesorach Intela), proces STOP rozpoczyna się, nic nie wykonuje i nigdy się nie kończy. Konstruktory SEQ i PAR służą odpowiednio do tworzenia zbioru procesów, które będą się wykonywać sekwencyjnie i zbioru procesów, które będą się wykonywać współbieżnie (równolegle). Zapis: SEQ x:=5 y:=x*4 2 oznacza, że obie instrukcje (procesy) wykonają się „jedna po drugiej”. Do określania bloku instrukcji w occamie służy wcięcie na dwie spacje . Instrukcje nie są zakończone średnikiem ani innym znakiem oprócz znaku końca wiersza. Zapis: PAR x:=2 y:=5 oznacza, że obie instrukcje (procesy) zostaną wykonane „jednocześnie”. Proces PAR zostanie zakończony w momencie zakończenia działania ich obu. Procesy zamknięte w bloku PAR nie mają wspólnych zmiennych i nie mogą się przez nie komunikować. Komunikacja musi odbywać się przez kanały. Do komunikacji dwukierunkowej należy użyć dwóch kanałów. Należy uważać, aby nie napisać kodu, który powodowałby zakleszczenie, np.: PAR SEQ kan1?x kan2!y SEQ kan2?y kan1!5 1 2 Komentarze w occamie rozpoczynają się ciągiem dwóch minusów (--) i kończą wraz z końcem linii, w której się znajdują. Pojęcie procesu w occamie jest trochę inne od tego, które znamy z systemów operacyjnych. W przypadku occama o procesie możemy myśleć jako o działaniu (akcji), które musi zostać wykonane. Nie jest to cecha tylko occama. Taka metoda oznaczania bloków instrukcji jest stosowana również w takich językach jak Haskell i Python. Wymusza to na programiście automatyczne formatowanie kodu. 1 Occam Zmienne i kanały muszą być zadeklarowane. W occamie istnieją następujące typy proste: BOOL (typ boolowski), BYTE (typ bajtowy), INT16, INT32, INT64 (typy całkowite, odpowiednio szesnasto-, trzydziestodwu- i sześćdziesięcioczterobitowe), REAL32 i REAL64 (typy zmiennoprzecinkowe odpowiednio trzydziestodwu- i sześćdziesięcioczterobitowe). Możemy również użyć typu INT, który w zależności od rodzaju transputera będzie oznaczał typ całkowity szesnasto- lub trzydziestodwubitowy. Przykład deklaracji zmiennej: INT16 x: Kanały deklarowane są w trochę odmienny sposób, np.: CHAN OF REAL64 k1: Occam pozwala również na deklarowanie tablic: [10] BYTE wektor: [10][10] BYTE macierz: Odwołanie do pojedynczego elementu tablicy wykonywane jest następująco: wektor[5]. Elementy tablic są indeksowane od 0, a liczba w nawiasach kwadratowych w definicji tablicy określa liczbę jej elementów. Zmienne widoczne są w obszarze bloku, w którym zostały zadeklarowane. Pętle w occamie tworzymy przy pomocy słowa kluczowego WHILE, np.: INT16 x: SEQ x:=0 WHILE x>=0 SEQ in?x out!x Procesy warunkowe są tworzone w occamie przy pomocy instrukcji IF. Należy pamiętać, aby uwzględnić w niej wszystkie możliwe warunki, inaczej ta instrukcja zamieni się w proces STOP. Niepoprawnym jest więc zapis: IF x>y k!1 Aby go poprawić trzeba napisać: IF x>y k1!5 TRUE SKIP Instrukcje IF mogą być zagnieżdżone. W occamie istnieje również instrukcja wielokrotnego wyboru CASE, np.: CASE direction up x:=x+1 down x:=x-1 Podobną w zapisie do instrukcji CASE, ale odmienną w działaniu jest instrukcja ALT. Dotyczy ona kanałów i umożliwia dokonanie wyboru w zależności od stanu kanału, np.: CHAN OF INT16 k1,k2,k3: INT x: ALT k1?x k3!x k2?x k3!x Jeśli gotów jest kanał „k1”, to z niego zostanie odebrana wartość i przesłana kanałem „k3”, jeśli pierwszy będzie jednak gotów kanał „k2”, to informacja odebrana z niego zostanie wysłana przez „k3”. Jeśli oba kanały są równocześnie gotowe, to wybierany jest jeden z nich. Aby ustalić priorytety kanałów można użyć instrukcji PRI, która działa również z konstruktorem PAR. Istnieje możliwość konstruowania tzw. instrukcji powtarzalnych PAR, SEQ, ALT i IF, np.: [5] CHAN OF INT32 kan: [5] INT32 x: 2 Occam PAR i=0 FOR 5 kan[i]!x[i] Liczba występująca po słowie kluczowym FOR oznacza liczbę powtórzeń instrukcji. Literały (wartości stałe) mogą być zapisywane zarówno jako wartości dziesiętne jak i szesnastkowe. W przypadku tych ostatnich przed wartością umieszcza się znak „#”. Liczby zmiennoprzecinkowe mogą być zapisywane w „zwykłej” notacji z kropką lub w notacji wykładniczej. W obu przypadkach należy za literałem, w nawiasach okrągłych umieścić nazwę typu. W przypadku liczb całkowitych domyślnie przyjmowany jest INT. Znaki są zapisywane w apostrofach, w zmiennych typu BYTE. Jeśli trzeba zapisać znak specjalny stawia się gwiazdkę przed nim np.: '*c' oznacza powrót karetki. Łańcuchy są zapisywane w tablicach bajtów i ujmowane są w cudzysłów. Zmienne boolowskie przyjmują tylko dwie wartości: TRUE i FALSE. Pole jest skończonym ciągiem elementów jednakowego typu. Polem jest również tablica. Można wyznaczyć również wycinek pola, np.: [tablica FROM 3 TO 5] lub bezpośrednio wymienić wartości należące do pola, np.: [1, 2, 3]. W occamie istnieją następujące operatory: „-” zmiana wartości liczby całkowitej na przeciwną, „~” - negacja bitowa liczby całkowitej, NOT – negacja logiczna, SIZE – odczyt wielkości pola (liczby jego elementów), MOSTPOS i MOSTNEG – największa i najmniejsza wartość liczby całkowitej, za operandem powinna znaleźć się nazwa typu całkowitego, „\” i REM - reszta z dzielenia, „+”, „-”, „*”, „/” - operacje arytmetyczne z testowaniem przepełnienia, PLUS, n MINUS, TIMES – opracje arytmetyczne modulo 2 , gdzie „n” jest liczbą bitów operandu całkowitego, „/\”, „\/”, „><”, odpowiednio AND, OR i XOR bitowy dla liczb całkowitych, „<<”, „>>”, przesunięcie bitowe w lewo i prawo, AND i OR logiczne (oba operandy typu BOOL), „=”, „<>”, „<”, „>”, „<=”, „>=” operatory relacyjne, AFTER – operator „później” dla wskaźników czasu. Konwersja typu REAL32 na REAL64 zachodzi automatycznie, w drugą stronę za wartością, którą chcemy skonwertować należy umieścić nazwę typu w nawiasach okrągłych. Do konwersji liczby zmiennoprzecinkowej na całkowitą służą instrukcje ROUND – zaokrąglenia i TRUNC – odcięcia części ułamkowej. W occamie istnieje specjalny typ TIMER. Zmienne tego typu są nazwane wskaźnikami czasu i można je traktować jako kanały do zegarów poszczególnych procesów. Impuls zegara dla procesów niskopriorytetowych wynosi 64 µs, a dla procesów wysokopriorytetowych 1µs. Wartości odczytane ze wskaźnika czasu są traktowane jako INT. Z wskaźnikami czasu można stosować operator AFTER, konstruując odpowiednie instrukcje opóźniające. Za pomocą protokołów określa się typ i strukturę przesyłanych danych. Dane te mogą być prostych typów jak np.: BYTE lub mogą być złożone, np.: CHAN OF [10] BYTE. W szczególnych przypadkach typ kanału może być nieokreślony, wówczas stosujemy słowo kluczowe ANY. Jeśli kanał jest wykorzystywany do przesyłania pól różnej długości, ale tego samego typu, to stosuje się tablice zliczające, np.: CHAN OF INT::[BYTE] dane: wówczas wysłanie przez taki kanał ma postać dane!:12::”Ala ma kota.” Protokołów można definiować, np. w ten sposób: PROTOCOL complex IS REAL32; REAL32: Occam pozwala również na definiowanie protokołów z wariantami, np.: PROTOCOL zbiory CASE start;BYTE nazwa;[20] BYTE rekord;INT32;int16;::[]BYTE stop : 3