/* >...< pogrubiê i nie zmieniaê tytuîu!*/ siEdem Rafaî Wiosna Witam wszystkich zwolenników E w siódmym juû wydaniu naszego cyklu. Rozpocznë bez zbëdnych wstëpów, przypominajâc o tym, ûeby dla rozluúnienia mózgownicy zapuôciê jakiô dobry muzak w tîo. Tym razem polecam krâûek Tori Amos "Under The Pink". W dzisiejszym odcinku rozwinë przedstawianie zestawu komend, rzâdzâcych przebiegiem programu. W poprzednich dwóch odcinkach wyjaôniîem zasadë obsîugi bîëdów, jak i konstrukcjë SELECT, teraz przyszîa pora na... IF Instrukcje warunkowe sâ jednymi z podstawowych konstrukcji uûywanych w programowaniu. To wiekopomne stwierdzenie nie wymaga chyba ûadnego komentarza. Przyjrzyjmy sië, "jak sië to robi w E": IF warunek_1 instrukcje_1 ELSEIF warunek_2 instrukcje_2 ELSE instrukcje_3 ENDIF Moûna to przetîumaczyê jako: ^* Jeôli 'warunek_1' jest speîniony (tzn. w wyniku daje TRUE lub dowolnâ niezerowâ wartoôê), to wykonaj 'instrukcje_1'. ^* Jeôli 'warunek_1' nie jest speîniony (tzn. w wyniku daje FALSE lub wartoôê zero), NATOMIAST 'warunek_2' jest speîniony, wykonaj 'instrukcje_2'. ^* Jeôli 'warunek_1' oraz 'warunek_2' nie sâ speînione, to wykonaj 'instrukcje_3'. Czëôê ELSEIF moûe byê powtarzana wiele razy. Natomiast czëôê ELSE moûna ominâê (wtedy nie bëdzie ûadnej reakcji na niespeînienie warunku), ale jeûeli jest, MUSI byê ona umieszczona na samym koïcu konstrukcji, zaraz przed zamkniëciem bloku IF...ENDIF. Natomiast czëôci ELSEIF moûe byê dowolna liczba, ale muszâ byê zawarte w bloku instrukcji IF...ELSE, co znaczy, ûe nie mogâ wystëpowaê w czëôci ELSE. Innâ formâ zapisu instrukcji warunkowej jest: IF warunek THEN instrukcja_1 ELSE instrukcja_2 Uûywajâc tej konwencji, nie moûna wykorzystywaê ELSEIF oraz wszystko naleûy umieôciê w jednej linii. Aby przybliûyê konstrukcjë IF...ELSEIF...ELSE...ENDIF, przytoczë kilka przykîadów jej uûycia: IF x>0 THEN x:=x+1 ELSE x:=0 IF x>0 x:=x+1 ELSE x:=0 ENDIF IF x=0 THEN WriteF('x to zero\n') IF x=0 WriteF('x to zero\n') ENDIF IF x<0 Write('x jest ujemne\n') ELSIF x>2000 Write('x jest za duûe\n') ELSIF (x=2000) OR (x=0) Write('Wartoôê graniczna x\n') ENDIF IF x>0 IF x>2000 WriteF('Za duûe x\n') ELSE WriteF('Dobre x\n') ENDIF ELSE IF x<-800 THEN WriteF('Za maîe x\n') ELSE Write('Ujemne, ale dobre x') ENDIF W ostatnim przykîadzie zastosowano tzw. zagnieûdûone bloki warunkowe (tzn. IF...ENDIF wewnâtrz innego bloku IF...ENDIF). Dla lepszego zaznaczenia tej sytuacji, a takûe do podkreôlenia czytelnoôci warto stosowaê wciëcia. Wtedy nie bëdzie problemów z rozróûnieniem zagnieûdûonych instrukcji ELSE (tzn. do którego IF-a sië stosuje). W tym miejscu naleûy zwróciê uwagë, ûe warunki zawarte po instrukcjach IF i ELSEIF nie powinny sië nakîadaê. Spójrzmy na przykîad: IF x>0 WriteF('x jest wiëksze od zera\n') ELSEIF x>200 WriteF('x jest wiëksze niû 200\n') ELSE WriteF('x jest za maîe\n') ENDIF Blok instrukcji po ELSEIF nie zostanie NIGDY wykonany, gdyû warunki x>0 i x>200 wzajemnie sië na siebie nakîadajâ, a dokîadniej warunek x>200 wynika z warunku x>0. Proszë zwróciê uwagë, ûe przy zamianie warunków konstrukcja ta bëdzie dobra. Konstrukcjë IF...THEN...ELSE (bez ENDIF!) moûna wykorzystaê nie tylko w blokach warunkowych, ale takûe przy obliczaniu wartoôci. Oto przykîad: IF x>0 y:=x+1 ELSE y:=0 ENDIF Takâ czësto spotykanâ konstrukcjë moûna zastâpiê jednâ liniâ kodu úródîowego: y:=(IF x>0 THEN x+1 ELSE 0) Umieszczenie IF...THEN...ELSE w nawiasie nie jest niezbëdne, ale poprawia czytelnoôê kodu programu. Taki zapis konstrukcji warunkowej róûni sië od przedstawionego wczeôniej gîównie jednâ rzeczâ: tutaj instruckja ELSE jest niezbëdna. Teraz czas na omówienie pëtli. FOR Pëtle pozwalajâ na wielokrotne wykonywanie serii instrukcji. Najprostszâ formâ pëtli jest pëtla FOR, szeroko znana z BASIC-a. Jeôli, nie wiadomo czemu, chcesz wypisaê na ekranie wszystkie liczby od jednego do stu, moûesz to zrobiê na dwa sposoby: albo wklepaê je rëcznie, zdzierajâc klawiaturë i palce, albo napisaê prosty program, który bëdzie uûywaî pëtli FOR: PROC main() DEF x FOR x:=1 TO 100 WriteF('\d ', x) ENDFOR WriteF('\n') ENDPROC Pamiëtaj o wpisaniu spacji po \d w czwartej linii! Po skompilowaniu zobaczysz na ekranie wielce zajmujâce rzâdki cyfr, tak jak chciaîeô. Program dziaîa nastëpujâco: ^* Definiowana jest zmienna lokalna o nazwie x (lokalna, gdyû definicja jest w ôrodku procedury). ^* Nastëpnie instrukcja FOR rozpoczyna pëtle i przyporzâdkowuje zmiennej x wartoôê poczâtkowâ, czyli 1. ^* Drukowana jest liczba, bëdâca wartoôciâ zmiennej x. ^* Instrukcja ENDFOR koïczy pëtlë, dodaje jeden do zmiennej x i sprawdza, czy przekroczony zostaî limit wartoôci x, okreôlony na poczâtku pëtli. Jeûeli nie, wykonywany jest skok do pierwszej instrukcji po FOR, jeûeli tak -- bieg programu nie zostaje zmieniony. W BASIC-u ENDFOR nazywa sië NEXT oraz wymaga podania nazwy zmiennej, która ma byê zwiëkszona i sprawdzona. W E nie przewiduje tego, tutaj ENDFOR zamyka ostatnâ pëtlë otworzonâ instrukcjâ FOR. ^* Na samym koïcu, ûeby îadnie zakoïczyê wydruk, drukowany jest znak nowej linii. Ogólna forma pëtli FOR...ENDFOR jest nastëpujâca: FOR zmienna := wartoôê_1 TO wartoôê_2 STEP liczba instrukcje ENDFOR Pamiëtaj, ûe zmiennâ o niezwykle mówiâcej nazwie 'zmienna' naleûy wczeôniej zdefiniowaê dyrektywâ DEF (to nie BASIC!) 'wartoôê_1'okreôla poczâtkowâ wartoôê zmiennej 'zmienna', nadawanâ przez FOR przy rozpoczëciu pëtli. 'wartoôê_2' okreôla limit, którego przekroczenie (tzn. zmienna>wartoôê_2) koïczy pëtlë. Czëôê 'instrukcje' bëdzie wykonywana aû do przekroczenia limitu. Opcjonalnie 'liczba' okreôla, ile ENDFOR ma dodaê do zmiennej 'zmienna' (przed sprawdzeniem limitu). Jeûeli nie wpiszemy kawaîka 'STEP coôtam', ENDFOR domyôlnie bëdzie dodawaîo jeden. 'wartoôê_1' i 'wartoôê_2' mogâ byê wyraûeniami, a nie tylko liczbami, ale w tym wypadku bëdâ obliczone tylko raz, na samym poczâtku, a wyniki zostanâ potraktowane jako liczby. 'liczba' moûe byê tylko i wyîâcznie staîâ, nie moûe byê obliczana, ale za to moûe byê ujemna, ale naleûy wtedy pamiëtaê, ûe zmienia sië warunek koïcowy (wtedy ENDFOR oblicza zmienna FOR zmienna := wartoôê_1 TO wartoôê_2 STEP liczba DO instrukcja Od tradycyjnego zapisu róûni sië tylko tym, ûe po 'DO' moûna wpisaê tylko jednâ instrukcjë. WHILE Pëtla WHILE róûni sië od FOR zasadniczo. Najwaûniejszâ róûnicâ jest to, ûe programista sam okreôla warunek koïczâcy wykonywanie pëtli. Druga waûna rzecz to fakt, ûe warunek jest za kaûdym razem obliczany i moûe byê okreôlony logicznie -- TRUE albo FALSE (co zresztâ kompilator zamienia na odpowiednio 1 i 0). Warunkiem moûe wiëc byê na przykîad wartoôê zwracana przez jakâô procedurë systemowâ. Na przykîad, pasjonujâcy program wypisujâcy na ekranie liczby od 1 do 100 moûna napisaê z wykorzystaniem pëtli WHILE: PROC main() DEF x x:=1 WHILE x<=100 WriteF('\d ', x) x:=x+1 ENDWHILE WriteF('\n') ENDPROC Wyglâda to na mniej efektywnâ konstrukcjë niû FOR...ENDFOR, ale w tym wypadku to prawda -- do prostych pëtli ze zwiëkszaniem/zmniejszaniem zmiennej o staîâ wartoôê FOR nadaje sië lepiej. Dziaîanie programu jest proste: zdefiniowana zmienna x przybiera wartoôê 1. Nastëpnie otwierana jest pëtla WHILE...ENDWHILE, wykonywana DOPÓKI speîniony jest warunek x<=100. W jej ôrodku wypisywana jest na ekran liczba okreôlona wartoôciâ zmiennej x, a potem zmienna ta zwiëkszana jest o jeden. Waûnâ wiadomoôciâ jest fakt, ûe warunek okreôlony w instrukcji WHILE sprawdzany jest PRZED wykonaniem pëtli. Oznacza to, ûe w ekstremalnym wypadku pëtla nigdy nie zostanie wykonana! Na przykîad, gdybyômy w powyûszym programie zmienili warunek na x>=100, pëtla nie byîaby wykonana, a bieg programu zostaîby przeniesiony za instrukcjë ENDWHILE. WHILE ma teû, jak inne omawiane konstrukcje, wersjë jednoliniowâ: WHILE warunek DO instrukcja Waûnym problemem, który pojawia sië przy wykorzystywaniu pëtli WHILE...ENDWHILE, a jak sië niedîugo okaûe nie tylko wtedy, jest zagroûenie wejôcia w pëtlë bez wyjôcia. Na przykîad, gdyby warunkiem WHILE byîo wyraûenie 1<2, instrukcje w ôrodku pëtli byîyby wykonywane aû do resetu komputera lub zatrzymania programu w sposób zewnëtrzny (np. poprzez 'zamroûenie' lub 'zabicie' odpowiedniego tasku programem ARTM). Oczywiôcie przykîad ten jest bezsensowny, gdyû nikt takiego warunku nie wpisze, ale moûe sië zdarzyê sytuacja, ûe warunek przybierze takâ postaê po jego obliczeniu. REPEAT Pëtla REPEAT...UNTIL jest bardzo podobna do WHILE...ENDWHILE. Tutaj jednakûe warunek jest obliczany PO wykonaniu caîej pëtli, w instrukcji UNTIL, która skacze na poczâtek pëtli wtedy, gdy warunek NIE JEST speîniony. Posîuûmy sië znowu nieômiertelnym programem 'Wypisz_Wszysykie_Liczby v1.0': PROC main() DEF x x:=1 REPEAT WriteF('\d ', x) x:=x+1 UNTIL x>100 WriteF('\n') ENDPROC Przy porównaniu dwóch ostatnich wersji tego programu widaê subtelnâ róûnicë miëdzy REPEAT...UNTIL a WHILE...ENDWHILE. Najwaûniejszâ róûnicâ jest fakt, ûe pëtla zostanie wykonana co najmniej raz. Przykîadem zastosowania WHILE w "prawdziwym" programie, korzystajâcym z systemu operacyjnego Amigi, moûe byê pëtla oczekujâca na wiadomoôci IDCMP. Jednoczeônie bardzo îadnie ona ilustruje wykorzystanie IF i ELSEIF: REPEAT mes:=Gt_GetIMsg(wnd.userport) type:=mes.class IF type=IDCMP_MENUPICK info:=mes.code menupick(info) /* procedura obsîugujâca sytuacjë, gdy uûytkownik wybraî coô z menu */ ELSEIF (type=IDCMP_GADGETDOWN) OR (type=IDCMP_GADGETUP) gadget:=mes.iaddress info:=g.gadgetid gadetaction(info) /* procedura obsîugujâca sytuacjë, gdy uûytkownik wcisnâî jakiô gadûet */ ELSEIF type=IDCMP_REFRESHWINDOW Gt_BeginRefresh(wnd) Gt_EndRefresh(wnd,TRUE) ENDIF Gt_ReplyIMsg(mes) UNTIL type<>IDCMP_CLOSEWINDOW Taka albo podobna pëtla stanowi zwykle najczëôciej wykonywany fragment programu komunikujâcego sië z uûytkownikiem za pomocâ GUI (graficznego interfejsu). * Ooops, ale sië zapëdziîem. Dobra wiadomoôê! Dotarîa juû do Polski wersja 3.1a Amiga E, a w niej SOURCE LEVEL DEBUGGER! Niestety, zmiejszono limit wielkoôci kodu wynikowego, generowanego przez kompilator w wersji nie zarejestrowanej, z 12 do 8 KB. A widzisz, nie trzeba byîo sië ociâgaê z kupnem rejestrowanej wersji E, to tylko 40 zielonych papierków (35 -- jeôli masz Internet)... Literatura: Jason R. Hulance, plik 'Beginners.guide' rozprowadzany z Amiga E 3.x.