/* wyraûenia zawarte w >...< proszë pogrubiê */ AMIGA E 8^) Witajcie kursanty! Jak to czas leci! Juû od póî roku i dwóch miesiëcy tîuczemy sië z Amiga E... Jest kwiecieï, warto wyjôê na powietrze, pojeúdziê na rowierze, oderwaê sië od komputerów... Jeûeli jednak tego nie moûesz zrobiê, polecam pîytkë Sheryl Crow o tytule "Tuesday Night Music Club". Album jest bardzo dobry, znalazîem na nim co najmniej trzy utwory o poziomie przewyûszajâcym zjechany do biaîoôci hit "All I Wanna Do". Poza tym panienka jest nie tylko îadna, ale teû umie ôpiewaê... Rafaî Wiosna Tak BTW*1: Naukowcy z Uniwersytetu Warszawskiego twierdzâ, ûe koderzy, którzy w przerwach w czasie programowania grajâ w grë Deluxe Galaga 2.5, sâ o 37% bardziej efektywni i robiâ o 17,3% mniej bîëdów. Zadziwiajâce, nieprawdaû?... 8^) Ciâgi Dziô zajmë sië gîëbiej typem danych, zwanych ciâgiem lub, z angielskiego, stringiem. Jest to jedna z najprostszych postaci tablicy, typu danych, którego omówienie zostawimy sobie na przyszîoôê. >Ciâg< w Amiga E to zestaw nastëpujâcych po sobie w pamiëci bajtów, bëdâcych najczëôciej normalnym napisem w kodzie ASCII, zakoïczony bajtem o wartoôci 0 ('\0'). Zmienna reprezentujâca ciâg ma wartoôê odpowiadajâcâ jego poîoûeniu (adresowi) w pamiëci. Spójrz na poniûszy program: PROC main() DEF s:PTR TO CHAR, i s:='To jest ciâg znaków' FOR i:=0 TO 19 WriteF('$\h : \z\d[3] "\c"\n',s+i,s[i],s[i]) ENDFOR WriteF('\n') ENDPROC W drugiej linii definiowana jest zmienna s jako wskaúnik do znaku. W nastëpnej jest jej nadawana wartoôê, bëdâca adresem ciâgu znaków, zawartego miëdzy apostrofami. Teraz, korzystajâz z pëtli FOR...ENDFOR, poznanej miesiâc temu, moûesz wypisaê nastëpujâce wartoôci: adres w zapisie heksadecymalnym, wartoôê bajtu wskazywanego przez ten adres oraz znak kodu ASCII, który reprezentuje të wartoôê. Zainteresowanym mogë powiedzieê, ûe zapis s[i] oznacza indeks tablicy (tutaj, i jest mnoûone przez 1, czyli dîugoôê jednego jej elementu; gdybyômy zadeklarowali s jako PTR TO WORD, kaûde i zostaîoby przemnoûone przez dwa -- czyli dîugoôê typu WORD). Amiga E wprowadza specjalny typ zmiennej: >E-ciâg< (E-string). Od normalnego ciâgu róûni sië wîaôciwie tylko nazwâ, gdyû E-ciâg moûe zastâpiê ciâg wszëdzie, gdzie zaûyczy sobie tego programista (w skîadni wywoîaï funkcji, które póúniej bëdâ zamieszczone, napis "ciâg" moûna dowolnie zamieniaê na "E-ciâg"). Nie moûna tego zrobiê odwrotnie -- w konstrukcjach, które wymagâjâ E-ciâgu, uûycie normalnego ciâgu jest niemoûliwe. Najwaûniejszâ róûnicâ jest fakt, ûe na E-ciâgu moûna wykonywaê dowolne operacje zmieniajâce jego dîugoôê, a na zwykîym ciâgu -- nie. E-ciâg wie, jakie sâ jego granice, a dokîadniej, pamiëta o tym generowany przez kompilator kod. E-ciâg definiowany jest jako typ STRING w nastëpujâcy sposób: PROC main() DEF s[20]:STRING, i StrCopy(s,'To jest ciâg znaków',ALL) FOR i:=0 TO 19 WriteF('\h : \z\d[3] "\c"\n',s+i,s[i],s[i]) ENDFOR WriteF('\n') ENDPROC Jak widzisz, definicja E-ciâgu wymusza na programiôcie okreôlenie jego maksymalnej dîugoôci. Spróbuj zmieniê liczbë 20 na 10 i zobacz, co sië stanie... Niektórzy AMOS-owcy mogâ zapytaê "co to za funkcja StrCopy() i dlaczego nie napisaîeô po prostu s:='To jest ciâg znaków'?" Jest to charakterystyczne pytanie dla programistów, majâcych stycznoôê z BASIC-em. Takie przyzwyczajenia mogâ prowadziê do powaûnych kîopotów z sercem oraz podwyûszonego poziomu agresji u programisty 8^). Otóû dlatego, ûe nie moûna PRZYPISYWAÊ CIÂGÓW do zmiennych zdefiniowanych jako E-ciâg. Zauwaû, ûe gdybym tak zrobiî, zmienna s wskazywaîaby wprawdzie na ciâg (a nie na E-ciâg), ale na tym by sië skoïczyîo -- ûadna operacja na zmiennej s, traktowanej jako wskaúnik do E-ciâgu, nie byîaby wykonalna, tzn. mogîaby prowadziê do zawieszenia sië komputera (ciâg nie ma jednej okreôlonej dîugoôci, której nie moûna zmieniaê; gdyby dodaê do niego drugi ciâg, okazaîoby sië, ûe po tej operacji zamazaîby kod znajdujâcy sië w pamiëci zaraz po nim). Funkcjë StrCopy() omówië juû zaraz. Moûe juû zauwaûyîeô, ûe nastëpnâ róûnicâ miëdzy E-ciâgiem a normalnym ciâgiem jest fakt, ûe przy definicji E-ciâgu zmienna automatycznie przybiera odpowiedniâ wartoôê, czyli wskazuje na tenûe E-ciâg. Przy definicji ciâgu zmienna, aû do chwili przypisania jej tegoû ciâgu, ma wartoôê nieokreôlonâ. Z tym wiâûe sië teû to, ûe nowo zdefiniowany E-giâg jest pusty, natomiast nowo zdefiniowany ciâg jest NIEOKREÔLONY. Chcesz sprawdziê? Zmieï drugâ linië ostatniego programu na: DEF s:PTR TO CHAR, i Twoje szczëôcie, ûe kompilator E nie ustawi tej zmiennej na 0, a gdzieô na obszar stosu, przeznaczonego dla tego programu, gdyû trzecia linia spowodowaîaby przepiëknâ wizytë naszego ulubionego Guru... (dla ambitnych: wyjaônijcie sobie dlaczego). Amiga E ma kilka zdefiniowanych funkcji, przeznaczonych do manipulacji na E-ciâgach. Pierwszâ z nich jest funkcja >String(MAXSIZE)<. Rezerwuje ona pamiëê na E-ciâg o dîugoôci MAXSIZE. Wartoôciâ funkcji jest adres kawaîka pamiëci. Jest interesujâce, ûe korzystajâc z tej funkcji, moûna zmieniê nieokreôlonâ zmiennâ typu PTR TO CHAR (ciâg) na zainicjowanâ zmiennâ typu STRING (E-ciâg). Gdyby dodaê linië: s:=String(20) do ostatniej wersji naszego przykîadowego programu (tej z DEF s:PTR TO CHAR _ORAZ_ StrCopy()), wpisujâc jâ miëdzy linie 2 i 3, okaûe sië, ûe zmienna s jest teraz wskaúnikiem do pustego E-ciâgu. Ûeby nie byîo nieporozumieï, zapis: DEF s[20]:STRING moûna zastâpiê dwoma liniami: DEF s:PTR TO CHAR s:=String(20) Funkcja ta umoûliwia dynamicznâ alokacjë E-ciâgów, których dîugoôci jeszcze nie znamy. Waûne jest to, ûeby sprawdziê, czy przypadkiem wynikiem funkcji nie bëdzie staîa NIL, która w Amiga E ma wartoôê 0. Jeûeli tak sië stanie, oznacza to fakt braku pamiëci na E-ciâg. Jeûeli to zignorujesz, moûesz sië spodziewaê szybkiego zawieszenia sië programu w sytuacji, gdy zabraknie miejsca. Moûna tego bardzo îatwo uniknâê, dodajâc linië: RAISE BLAD_BRAKPAMIECI IF String()=NIL na poczâtku programu. (Pamiëtaj, ûeby zdefiniowaê ten numer bîëdu w poczâtkowych ENUM-ach oraz odpowiednio na niego reagowaê w handlerze bîëdów). Czyû obsîuga sytuacji podbramkowych w Amiga E nie jest piëkna? Moûe nie, ale na pewno pozwala programiôcie skoncentrowaê sië na treôci, a nie na formie... Funkcjë StrCopy() juû raz zastosowaîem. Ma ona nastëpujâcâ skîadnië >StrCopy(E-ciâg,ciâg,dîugoôê=ALL)<. Jej zadaniem jest skopiowanie ciâgu (lub E-ciâgu) do E-ciâgu. Funkcja zwraca wskaúnik do wyniku. Spójrz na przykîady jej uûycia: DEF s[20]:STRING StrCopy(s,'To jest ciâg znaków',ALL) StrCopy(s,'A to jest ciâg dluûszy niû 20 znaków',ALL) StrCopy(s,'1234567890',7) StrCopy(s,'',ALL) Pierwszy przykîad skopiuje znaki z ciâgu, bëdâcego drugim parametrem funkcji, do E-ciâgu, bëdâcego jej pierwszym parametrem. Skopiowane zostanâ wszystkie znaki ciâgu, a to dziëki temu, ûe ostatni parametr ma umownâ wartoôê ALL. W drugim przykîadzie, do E-ciâgu s zostanie skopiowanych tylko 20 pierwszych znaków ciâgu "A to jest...". Dlaczego? Dlatego, ûe kompilator pamiëta o definicji E-ciâgu s, którego dîugoôê nie moûe przekraczaê 20 bajtów. Trzeci przykîad skopiuje tylko 7 znaków z ciâgu, zawierajâcego cyferki. Jeûeli przykîady 2. i 3. zostanâ wykonane w programie po sobie, E-ciâg s po tym bëdzie miaî dîugoôê 7 znaków, a nie 20. StrCopy() kopiuje ciâgi, a nie wkleja ich! Ostatni przykîad kopiuje ciâg pusty. W wyniku tego E-ciâg s bëdzie miaî dîugoôê 0. Umowna dyrektywa ALL nie jest konieczna w Amiga E 3.x, które wprowadziîo parametry domyôlne. Dziëki temu jeûeli w funkcji StrCopy() sâ tylko dwa parametry, kompilator rozszerza jâ o trzeci, domyôlny, którego wartoôciâ jest ALL. Zaoszczëdza to pisania: StrCopy(s,'ciâg',ALL) to to samo w Amiga E 3.x co StrCopy(s,'ciâg') Modyfikacjâ StrCpy() jest funkcja >RightStr(E-ciâg1,E-ciâg2,dîugoôê), która kopiuje znaki z koïca drugiego E-ciâgu do pierwszego. Przykîad: DEF s[20]:STRING, t[20]:STRING StrCopy(t,'1234567890') RightStr(s,t,1) WriteF('"\s"\n',s) -> "0" RightStr(s,t,7) WriteF('"\s"\n',s) -> "4567890" Komentarze pokazujâ postaê E-ciâgu s po wykonaniu funkcji RightStr(). Nie moûna w niej uûyê dyrektywy ALL zamiast konkretnego okreôlenia dîugoôci kopiowanych znaków. Byîoby to bez sensu i stanowiîoby powielenie funkcji StrCopy(). Jeûeli juû mówimy o dîugoôci ciâgów, warto poznaê funkcjë pozwalajâcâ na jej zmierzenie(?). >StrLen(ciâg)< w wyniku zwraca dîugoôê ciâgu (lub E-ciâgu). W wypadku E-ciâgów lepiej uûyê funkcji >EstrLen(E-ciâg)<, która nie bëdzie poszukiwaîa bajtu o wartoôci 0 w podanym ciâgu, a zwróci jego dîugoôê natychmiast. Jest to nastëpna zaleta E-ciâgów. Podobnâ funkcjâ jest >StrMax(E-ciâg)<, która to zwraca maksymalnâ dîugoôê E-ciâgu bëdâcego parametrem. W wiëkszoôci wypadków, jak moûna sië tego spodziewaê, prawdziwa dîugoôê nie bëdzie równa maksymalnej. Bardzo przydatnâ funkcjâ jest >StrCmp(ciâg,ciâg,dîugoôê=ALL)<. Porównuje ona dwa (E-)ciâgi i zwraca wartoôê TRUE, jeûeli sâ one jednakowe, lub FALSE, jeûeli sâ róûne. Po co taka funkcja? Czy nie proôciej napisaê IF s=e THEN... Otóû znowu tak moûna zorganizowaê porównywanie ciâgów w BASIC-u, a nie w Amiga E. Tak sformuîowana instrukcja warunkowa porówna ADRESY (E-)ciâgów, a nie ich ZAWARTOÔC! Najlepiej dziaîanie funkcji zobrazujâ przykîady. Wszystkie poniûsze wywoîania StrCmp() dajâ w wyniku wartoôê TRUE: StrCmp('ABC', 'ABC') StrCmp('ABC', 'ABC', ALL) StrCmp('ABCd', 'ABC', 3) StrCmp('ABCde','ABCxxjs',3) A teraz przykîady przy których StrCmp() zwraca FALSE: StrCmp('ABC', 'ABc') StrCmp('ABC', 'ABc', ALL) StrCmp('ABCd', 'ABC', ALL) Kolejnâ funkcjâ jest >StrAdd(E-ciâg,ciâg,dîugoôê=ALL), która dodaje (E-)ciâg do E-ciâgu: DEF s[20]:STRING StrCopy(s,'12345') StrAdd(s,'67890',ALL) WriteF('"\s"\n',s) -> "1234567890" StrAdd(s,'ABDEF',3) WriteF('"\s"\n',s) -> "1234567890ABD" Bardziej zaawansowanâ funkcjâ operujâcâ na (E-)ciâgach jest >MidStr(E-ciâg,ciâg,pozycja,dîugoôê=ALL). Ma ona za zadanie skopiowanie znaków z (E-)ciâgu, bëdâcego drugim parametrem, do pierwszego parametru -- E-ciâgu. Skopiowane zostanie tyle znaków, ile okreôlimy w czwartym parametrze. Z (E-)ciâgu bedâcego drugim parametrem bëdâ one kopiowane od pozycji okreôlonej parametrem trzecim. Najlepiej pokaûâ to przykîady: DEF s[20]:STRING MidStr(s,'1234567890',2,ALL) WriteF('"\s"\n',s) -> "34567890" MidStr(s,'1234567890',6,3) WriteF('"\s"\n',s) -> "789" Jak widaê, pozycje w ciâgu liczy sië od zera. Bardzo przydatnâ funkcjâ jest >InStr(ciâg1,ciâg2,pozycja=0)<. Przeszukuje ona (E-)ciâg1 pierwszy i sprawdza, czy nie wystâpiî (E-)ciâg2. Poszukiwania moûna zaczâê od okreôlonej trzecim parametrem pozycji (E-)ciâgu1 (jeûeli tego parametru brak, szukanie zacznie sië od samego poczâtku). InStr() zwraca pozycjë, na której zostaî znaleziony (E-)ciâg2 (UWAGA! moûe to byê 0!) lub -1, jeûeli go nie znaleziono. Jak moûna sië domyôliê, przy poszukiwaniu rozróûniane sâ litery duûe i maîe (case sensitive). A propos wielkoôci liter, to dwie funkcje, >LowerStr(ciâg)< i >UpperStr(ciâg)< zamieniâ twój (E-)ciâg odpowiednio na maîe i duûe literki. Równieû przydatnâ funkcjâ jest >TrimStr(ciâg)<. Zwraca ona adres pierwszego "drukowalnego" znaku w (E-)ciâgu bëdâcym jej parametrem. Przeskakiwane sâ spacje i znaki poniûej tej funkcji w tabeli ASCII (czyli poniûej 32). Oto przykîad: DEF s[20]:STRING StrCopy(s,' \n \t HEJ',ALL) WriteF('"\s"\n',s) -> "{cr} HEJ" WriteF('"\s"\n',TrimStr(s)) -> "HEJ" W miejscu oznaczonym {cr} kursor przesunie sië na poczâtek nastëpnej linii. Mniej wykorzystywanâ funkcjâ jest >SetStr(E-giâg,dîugoôê)<. Zmienia ona dîugoôê E-ciâgu na okreôlonâ drugim parametrem. Oto przykîad uûycia: DEF s[10]:STRING StrCopy(s,'1234567890') WriteF('"\s"\n',s) -> "1234567890" SetStr(s,5) WriteF('"\s"\n',s) -> "12345" SetStr(s,15) WriteF('"\s"\n',s) -> "12345" StrCopy(s,'12345678901234567890') WriteF('"\s"\n',s) -> "1234567890" Ostatniâ omówionâ przeze mnie funkcjâ, zwiâzanâ z (E-)ciâgami jest >Val(ciâg,adres=NIL)<. Ma ona za zadanie zamianë ciâgu znaków, w którym znajduje sië liczba na wartoôê typu LONG. Val() zignoruje wszystkie spacje na poczâtku, a znaki "%" i "$" przed liczbami zinterpretuje jako poczâtek zapisu dwójkowego lub heksadecymalnego. Funkcja zwraca wartoôê odczytanâ z (E-)ciâgu. Jeûeli drugi argument jest podany, to zmiennej WSKAZYWANEJ przez niego zostanie wpisana liczba znaków, które funkcja Val() musiaîa przejrzeê, aby obliczyê zwróconâ wartoôê. Na pierwszy rzut oka wyglâda to na bardzo skomplikowane, ale w "praniu" okazuje sië, ûe nie taki diabeî straszny. Przeôledúmy przykîadowy kawaîek programu: DEF s[30]:STRING, value, chars, p:PTR TO CHAR StrCopy(s,' \t \n 10 \t $3F -%0101010') value:=Val('abcde 10 20',{chars}) -> ADRES zmiennej chars! Po wykonaniu powyûszej linii zmienna "value", jak równieû zmienna chars, bëdzie miaîa wartoôê 0. Dlaczego? Poniewaû 'abcde' to nie jest liczba dla Val(). (Byîaby, gdyby dodaê na poczâtku "$"!). value:=Val(s, {chars}) Teraz "value" ma wartoôê 10, a chars -- 7 (liczba znaków przejrzanych przez funkcjë Val()). p:=s+chars Teraz ciâg p wskazuje na siódmy znak E-ciâgu s. value:=Val(p,{chars}) Po tym "value" przybierze wartoôê 63 (czyli $3F), a zmienna chars -- 6 (przejrzano nastëpne 6 znaków). p:=p+chars Teraz ciâg p wskazuje na spacjë zaraz po "$3F" w E-ciâgu s. value:=Val(p,{chars}) Teraz "value" ma wartoôê -42 (-%0101010), a chars -- 10. Powyûszy przykîad bardzo îadnie ilustruje, gdzie naleûy uûyê ciâgu, a gdzie E-ciâgu. * Oops, to by byîo na tyle z (E-)ciâgami. Sâ jeszcze dwie funkcje z nimi zwiâzane (ReadStr() i StringF()), ale zajmë sië nimi dopiero przy omawianiu operacji wejôcia/wyjôcia. Na dziô to juû koniec. Przepraszam, ûe nie ma comiesiëcznego listingu do wklepania, ale ta czëôê kursu E tak sië rozrosîa, ûe nie ma na niego juû miejsca... BYE4NOW! *1 BTW -- By The Way, odpowiednik polskiego "swojâ drogâ". Literatura: Jason R. Hulance, plik "Beginners.guide" rozprowadzany z Amiga E 3.x.