FORTH - część 4Zacytuję: Chciałem Pana poinformować, że języki FORTH, FORTRAN nie istnieją w wersji na Amigę. Myślę że nie ma sensu programować w języku FORTH, jeżeli on praktycznie wychodzi z użytku. Mam nadzieję, że użytkownicy komputerów ATARI XL/XE nie podzielają poglądów zawartych w tych słowach. Aby rozwiać całkowicie wszelkie wątpliwości, postanowiłem skomentować powyższy fragment listu. Po pierwsze: Języki FORTH i FORTRAN łączą cztery rzeczy: F, O, R i T. Poza tym, są to języki całkowicie odmienne. Po drugie: Sądzę, że język FORTH nie zdążył jeszcze dobrze wejść do użytku, aby miał już z niego wychodzić. Ostatni standard pochodzi z roku 1983 i trudno tu mówić o czymś przestarzałym. A swoją drogą: oczywiście, język FORTH istnieje w wersji na Amigę. Przystąpmy jednak do kolejnej odsłony, ukazującej tajniki naszego języka. ZADANIEPĘTLEZacznijmy od tej najprostszej, a przy tym - najmniej pożytecznej. Ma ona następującą postać: BEGIN ... AGAIN Pętla ta wykonuje bez końca wszystkie słowa zawarte między BEGIN a AGAIN. Uwaga! Wszystkie pętle w języku FORTH muszą występować wewnątrz zadeklarowanego słowa. Niedopuszczanie jest użycie takiej pętli w trybie bezpośrednim, np: BEGIN 1 . AGAIN RETURN Każda pętla musi mleć swój początek i koniec wewnątrz jednego słowa, np: : PET BEGIN 1 . AGAIN ; RETURN Wykonajmy teraz nasze słowo pisząc: PET RETURN Słowo to będzie się wykonywało w nieskończoność i nie pomoże nam naciskanie klawisza BREAK. Jedynym ratunkiem jest niezastąpiony RESET, po którym FORTH wróci do stanu początkowego. Następną pętlą, którą chciałbym przedstawić jest tzw. pętla liczona. Ma ona następującą postać: n2 n1 DO ... LOOP n1 - to liczba określająca wartość początkową licznika pętli, znajdująca się na szczycie stosu n2 - wartość końcowa licznika pętli, znajdująca się na stosie pod n1 DO - otwiera pętle liczoną ... - tu znajdują się dowolne słowa wewnątrz pętli LOOP - zamyka pętlę liczoną Oczywiście, pętla ta może występować tylko wewnątrz zadeklarowanego słowa. Przykład: : PEL 20 0 DO 1 . LOOP ; RETURN i sprawdzamy: PEL RETURN Na ekranie ukazało się 20 jedynek. Aby uzyskać dostęp do licznika pętli wprowadzono słowo I. Wywołanie tego słowa (tylko wewnątrz pętli liczonej) zostawia na stosie aktualny stan licznika pętli. Przykład: : PET 20 0 DO I . LOOP ; RETURN i wywołujemy: PET RETURN Na ekranie ukazały się liczby od 0 do 19, a nie do 20, jak byśmy się spodziewali. Pętla była wykonywana, dopóki licznik nie osiągnął wartości końcowej, czyli w naszym przypadku 20. Wartość licznika jest zwiększana w słowie LOOP, tam też jest sprawdzany warunek końcowy. W przypadku osiągnięcia przez licznik wartości końcowej pętla zakończy się. FORTH rozporządza także bardziej skomplikowaną odmianą pętli liczonej. Ma ona następującą postać: n2 n1 DO ... n +LOOP n2 i n1 -to liczby na stosie mające identyczne znaczenie jak w poprzedniej pętli, DO - otwiera pętlę ... - słowa wewnątrz pętli n - liczba określająca skok licznika pętli. Musi zawsze występować przed +LOOP a może przyjmować dowolne wartości, także ujemne. +LOOP - słowo kończące pętlę Podczas korzystania z tej pętli należy uważać, aby licznik zbliżał się do górnej granicy, a nie oddalał. Błędny byłby zapis: : PEM 20 0 DO I . -1 +LOOP ; Wartość początkowa licznika wynosi O, a z każdym krokiem pętli licznik jest zwiększany o -1, czyli zmniejszany. Licznik zamiast zbliżać się do górnej granicy, oddala się od niej. Pętla ta nie zostanie nigdy zakończona. Poprawny będzie jednak następujący zapis: : PEM 0 20 DO I . -1 +LOOP ; RETURN Wartością początkową jest 20, a końcową 0. Słowo to po wywołaniu wyświetli liczby od 20 do 1. Skoro poznaliśmy już pętle liczone, przeprowadźmy test sprawdzający szybkość języka FORTH na naszym komputerze. Deklarujemy słowo TEST: : TEST 30000 0 DO LOOP ; RETURN Wywołując słowo ze stoperem w ręku, możemy określić czas jego wykonania. Gdy ukaże się napis OK, oznacza to zakończenie działania słowa. Aby uzyskać większą dokładność pomiaru czasu, możemy wykorzystać stworzone w poprzednich odcinkach słowa: OZEGAR! i ZEGAR@ . Piszemy sekwencję: 0ZEGAR! TEST ZEGAR@ . RETURN Na ekranie pojawiła się liczba, która podzielona przez 50 da nam czas w sekundach. Tak, to nie przywidzenie, pętla wykonywała się około 3 sekund. Dla porównania, podobna pętla (FOR NEXT) w języku ATARI BASIC jest liczona 58 sekund. PORÓWNANIAOperator ten pobiera i porównuje dwie liczby będące najbliżej szczytu stosu. Na ich miejscu zostawia jedną liczbę będącą tzw. znacznikiem porównania. Znacznik ten przybiera wartość 0 w przypadku fałszu i 1 dla prawdy. W języku FORTH każda liczba niezerowa jest znacznikiem prawdy. Należy o tym pamiętać, gdyż jak przekonamy się dalej. Jako znacznik można wykorzystać wynik z dowolnego działania matematycznego. Kolejnymi słowami porównania będą operatory: < - mniejszości > - większości Słowa te działają na stosie bardzo podobnie do operatora równości, lecz zostawiany wskaźnik jest obliczany następująco: Dla < znacznik przybiera wartość 1 (czyli prawdy), gdy liczba znajdująca się niżej na stosie jest mniejsza od liczby będącej na jej szczycie. Dla > znacznik wyniesie 1 (prawda), gdy liczba w głębi stosu jest większa od liczby na szczycie. Sprawdźmy to w praktyce pisząc na komputerze: 10 5 = . RETURN Otrzymamy wynik 0. Jeszcze raz: 10 10 = . RETURN Tym razem otrzymamy oczywiście l. Dla nierówności: 10 5 > . RETURN jest l, a dla 10 5 < . RETURN oczywiście 0. Zauważmy, że znaki porównania wystąpiły w zapisie za liczbami, a nie między nimi. Wynika to z zastosowanej notacji zapisu działania (tzw. Odwrotnej Notacji Polskiej). Poznane dotychczas porównania interpretują dane wejściowe jako liczby ze znakiem. Aby umożliwić porównanie liczb bez znaku, należy zastosować słowo U< . Działa ono podobne jak <, lecz nie uwzględnia znaku liczby. Pamiętajmy o tym, że każda liczba większa od 32767 jest interpretowana przez większość słów jako ujemna. Była o tym mowa w poprzednich odcinkach tego cyklu. Spróbujmy: 20000 45000 < . RETURN Otrzymaliśmy wynik 0, a powinno być 1. Tak, lecz słowo < interpretuje liczby jako ujemne o całkowicie innych wartościach. Sprawdźmy, jak widzi te liczby słowo <. Piszemy: 20000 . RETURN i 45000 . RETURN Widzimy, że nasze słowo zachowało się całkowicie poprawnie. Ten sam warunek powinien wyglądać następująco: 20000 45000 U< . RETURN Teraz otrzymaliśmy wartość 1. Słowo U< znajduje szerokie zastosowanie przy porównywaniu adresów, które - jak wiemy - są liczbami dodatnimi. Operatory przedstawione poniżej wymagają tylko jednego parametru wejściowego. Są nimi: 0- i 0< Wykonują one porównanie liczby będącej na szczycie stosu z zerem. Operator 0= pobiera liczbę ze szczytu stosu i zostawia na jej miejsce znacznik równy 1 (prawda), gdy liczba równa się 0, a 0 (fałsz), gdy jest inaczej. Operator 0< zostawia na stosie znacznik prawdy (1) w przypadku, gdy liczba na stosie była ujemna. Słowo 0= pełni w języku FORTH także funkcję operatora NOT, znanego nam z innych języków. Umożliwia ono zamianę znacznika (prawda) 1 na (fałsz) 0 lub odwrotnie. Czytelnicy zauważyli zapewne, że nie ma wśród standardowych słów języka FORTH słowa U> lub 0>. Co zrobić w przypadku, gdy zaistnieje konieczność wykonania takiego porównania? Nie ma z tym żadnych kłopotów, gdyż FORTH jest językiem umożliwiającym samodzielne tworzenie brakujących słów. Przykład: : 0> 1 - 0< 0= ; Zapewne zrozumiałe jest dla czytelników zastosowanie w prezentowanej deklaracji słowa 0<, a następnie zaprzeczenie znacznika przez 0=. Po co jednak odjęliśmy 1 od liczby na stosie? Gdyby opuścić tę operację, powstałoby następujące słowo: : 0>= 0< 0= ; Słowo to zostawiałoby znacznik prawdy także w przypadku, gdy liczba ze szczytu stosu równałaby się 0. Mam nadzieję, że ze słowem U> czytelnicy poradzą sobie sami. Kończąc opisywanie porównań, chciałbym zwrócić uwagę, że porównania wymagają zawsze parametrów na stosie. Niedopuszczalny byłby zapis: 10 5 < = gdyż dla słowa = zabraknie jednego parametru. W takim przypadku należy zastosować: 10 5 > 0= Aby zwiększyć czytelność możemy stworzyć odpowiednie słowo: : <= > 0= ; RETURN i zapisać: 10 5 <= PĘTLE, CIĄG DALSZY...BEGIN ... f UNTIL BEGIN - początek pętli ... - jak w poprzednich przykładach f - znacznik logiczny zostawiany na stosie UNTIL - koniec pętli Pamiętajmy, że znacznik f musi być zostawiany na stosie po każdym przebiegu pętli, gdyż słowo UNTIL pobiera go ze stosu. Pętla ta jest wykonywana tak długo, aż znacznik f przybierze wartość różną od zera (prawda). Słowa będące wewnątrz pętli muszą ten znacznik odpowiednio modyfikować. Przykład: : ES 255 764 C! BEGIN 764 C@ 28 = UNTIL ; RETURN Zadeklarowaliśmy słowo o nazwie ES. Co ono wykonuje? Sprawdźmy: ES RETURN Nic się nie dzieje, dopiero naciśnięcie klawisza ESC powoduje opuszczenie słowa. Jedyną jego funkcją jest właśnie czekanie na naciśnięcie tego klawisza. Ostatnia z prezentowanych pętli ma postać: BEGIN ... f WHILE ... REPEAT BEGIN - początek pętli ... - ciąg słów f - znacznik logiczny na stosie WHILE - słowo pobierające i sprawdzające znacznik. Gdy jest on równy 0 (fałsz), pętla jest opuszczana (skok za słowo REPEAT) REPEAT - kończy pętlę Zauważmy, że pętla ta różni się od poprzedniej, nie tylko odmiennym interpretowaniem znacznika, ale także dowolnością umieszczenia słowa WHILE sprawdzającego warunek. Jeżeli umieścimy WHILE na początku pętli (zaraz po słowie BEGIN), może ona nie być wykonana ani raz, podczas gdy BEGIN UNTIL musi się wykonać co najmniej raz. Przykład: : AA 255 764 C! BEGIN 1 . 764 C@ 255 = WHILE 2 . REPEAT ; RETURN Nowo stworzone słowo AA wyświetla kolejno pary liczb 1 i 2. Gdy naciśniemy dowolny klawisz (komórka 764 przyjmie wartość różną od 255), pętla zostanie opuszczona. W ostatnim przebiegu pętli, słowa znajdujące się po WHILE nie będą wykonane (liczba 2 nie zostanie wyświetlona). JESZCZE O STOSIEDUP Stowo to tworzy duplikat liczby znajdującej się na szczycie stosu i zostawia go nad tą liczbą, powiększając stos. SWAP Zamienia między sobą dwie liczby znajdujące się najbliżej szczytu stosu. DROP Usuwa liczbę ze stosu. OVER Tworzy kopię liczby znajdującej się pod liczbą ze szczytu i umieszcza ją nad liczbą ze szczytu, powiększając odpowiednio stos. Liczby znajdujące się dotychczas na stosie nie ulegają zmianie. ROT Ustawia trzy liczby znajdujące się najbliżej szczytu w ten sposób, że trzecią wyciąga na szczyt, a dwie pozostałe obniża o jedno miejsce. Przetestujmy działanie dwóch ostatnich słów, gdyż wydają się one najtrudniejsze do zrozumienia. Piszemy: 2 3 OVER . . . RETURN Uzyskaliśmy ciąg liczb: 2 3 2. Liczba 2 została skopiowana nad dotychczasowym szczytem stosu, czyli liczbą 3. Następny przykład: 1 2 3 ROT . . . RETURN Tym razem zobaczymy 132. Pamiętajmy, że szczyt stosu przy wprowadzaniu znajduje się po prawej stronie ciągu i jest nim liczba 3. Przy wyświetlaniu - odwrotnie, gdyż najpierw będzie pobrana liczba ze szczytu. Widzimy więc, że liczba 1 przeskoczyła z trzeciego miejsca na szczyt stosu. Jeżeli twój FORTH posiada słowo S. lub .S to możesz je stosować do przeglądania zawartości stosu. REALIZACJA ZADANIADo napisania słowa PAUSE wykorzystamy wcześniej stworzone słowa 0ZEGAR! i ZEGAR@. Były one opisane w poprzednich odcinkach tego cyklu. Więc do dzieła! : PAUSE 0ZEGAR! BEGIN DUP ZEGAR@ = UNTIL DROP ; 0ZEGAR! - zeruje stan zegara BEGIN - otwiera pętlę DUP - wykonuje duplikat parametru wejściowego ZEGAR@ - zostawia na stosie stan zegara = - porównuje wartość pobraną z komórek zegara z parametrem wejściowym UNTIL - powoduje opuszczenie pętli, gdy znacznik logiczny pozostawiony na stosie przez = ma wartość niezerową (prawda) Nasze nowe słowo PAUSE wymaga zawsze parametru wejściowego, np.: 50 PAUSE RETURN Liczba występująca przed PAUSE określa czas działania słowa. Dla wartości równej 50 będzie on wynosił dokładnie 1 sekundę. Sposób realizacji słowa PAUSE, przedstawiony przeze mnie, nie jest najlepszy, gdyż ingeruje w stan zegara. Mam nadzieję, że czytelnicy potrafią sami rozwiązać ten problem w taki sposób, aby wartość komórek zegara nie ulegała zmianie. Proszę więc o wasze rozwiązania słowa PAUSE, oczywiście w języku FORTH. Najlepsze z nich zostaną zaprezentowane na łamach TA. Roland Pantoła
Sprostowanie Do poprzedniego odcinka opowieści o języku FORTH wkradło się kilka nieścisłości. Rada dla posiadaczy magnetofonów (str. 24) okazała się nietrafna. Zapisanie FORTH-a ze zmienioną wartością WARNING nic nie da, ponieważ FORTH odnawia jej stan po wprowadzeniu go do komputera. Należy więc każdorazowo ustawiać ją ręcznie. Pod hasłem Działania pierwszy wiersz powinien wyglądać: + - * / n1 n2 --- wynik Złośliwy chochlik "złamał" ten wiersz na dwie części (inny złośliwy chochlik dopisał literkę i, która oczywiście nie jest symbolem działania. Przepraszamy. Poszukiwania winnych trwają. Redaktor
|