„Kukiełka Marionetkarza: o byciu ekspertem w programowaniu komputerowym”
- CAPUT I NARODZINY TYRANII Z DUCHA SPRAWCZOŚCI
- CAPUT II DŻIHAD USPRAWIEDLIWIA ISKRĘ, OD KTÓREJ ŚWIAT STAJE W OGNIU
- CAPUT III STATYSTYCZNIE ISTOTNY POMIAR WŁASNEGO CIENIA
- CAPUT IV ANALIZA UKŁADU ZAMKNIĘTEGO Z PERSPEKTYWY WIĘŹNIA
- CAPUT V ZWIEDZANIE RAJU UTRACONEGO
- CAPUT VI SŁOŃ JAKI JEST KAŻDY WIDZI, WIĘC UPAJAJMY SIĘ!
- CAPUT VII TRANSCENDENCJA NIEOSIĄGALNA
- CAPUT VIII ENDOMORFIZACJA JAJKA KOLUMBA
- CAPUT IX CZŁOWIEK CZŁOWIEKOWI RZEMIEŚLNIKIEM RZECZYWISTOŚCI
- CAPUT X KAŻDY PUNKT DROGI TO POCZĄTEK I KONIEC
- APPENDIX A WYBRANE FRAGMENTY DOKUMENTACJI WINDRAD
- APPENDIX B WYBRANE FRAGMENTY DOKUMENTACJI WISŁA 64
- APPENDIX C WYBRANE FRAGMENTY DOKUMENTACJI DRZWI
MARJONETKA KVKIEŁKARZA
albo
O BYCIU EKſPERTEM
W ZAUTOMATYZOWANYM
PRZETWARZANIU DANYCH
ZA POMOCĄ PROGRAMOWANYA
MASZYN KALKULUJĄCYCH
F.N.
CAPUT I
NARODZINY TYRANII
Z DUCHA SPRAWCZOŚCI
Kiedy zaczynałem swoją pierwszą pracę jako
programista, najbardziej popularnym
kalkulatorem w naszym kraju była Wisła 64.
Posiadała ona zawrotne 65536 adresowalnych
rejestrów pamięciowych oraz jednostkę
arytmetyczno-logiczną wspierającą operacje na
16-cyfrowych słowach binarnych. Model
programowy był wyjątkowo przyjazny,
ponieważ dla niemal wszystkich operacji adres
rejestru danych był kodowany w ostatnich dwóch oktetach instrukcji.
Był to dramatyczny przeskok względem kalkulatora Orzeł 8, który
dominował tuż przed Wisłą. Mimo 8-cyfrowych binarnych operacji
arytmetycznych Orzeł umożliwiał adresowanie 16384 oktetów pamięci,
ale opierało się to na skomplikowanym systemie banków pamięci.
Dla starych wyg Orła, którzy opanowali jego model, ta zmiana faworyta
była niemal jak śmierć przyjaciela. Koniec końców otrząsnęli się oni z
żałoby i zaczęli wykorzystywać potencjał nowej maszyny.
Miałem to szczęście, że wydział na którym studiowałem matematykę
stosowaną ze specjalizacją w systemach cyfrowych zdążył zaopatrzyć się
w Wisłę 64, gdy byłem na ostatnim roku mojego programu. Dzięki
czemu udało mi się skosztować nieco świeżonki przed wylądowaniem
na rynku pracy.
Co prawda moim szczytem wykorzystania tego systemu było rysowanie
drzewek w konsoli terminala. Uczelniani operatorzy, którym donosiłem
karty z wyperforowanym programem, sarkastycznie pytali, czy
stworzyłem nowy program albo znowu jest to tylko rysowanie
Fryckowych krzaków. Żółć, która mnie od tego zalewała,
odparowywała w kontakcie z entuzjazmem wynikającym z obcowania z
cudem ludzkości, jakim była Wisła 64.
Spółka PolACME, w której zatrudniłem się jako inżynier, dostarczała
rozwiązań obliczeniowych podmiotom w całym kraju oraz powoli
zdobywała zagraniczne rynki. Naszym sztandarowym produktem był
program Drzwi. Dedykowany był on na systemy oparte o Wisła 64 i
stanowił wejście do wirtualnej przestrzeni operatorskiej, która
umożliwiała wykonywanie wielu pomniejszych programów naraz oraz
dostarczała szereg wygodnych narzędzi do zarządzania pamięcią
operacyjną, jak i pamięcią pomocniczą. Wspierała zarówno taśmy
magnetyczne, jak i magazyny talerzowe. Był to produkt tak świetny, że
używali go nawet nasi inżynierowie i projektanci.
Dział, w którym pracowałem, opracowywał innowacyjne urządzenie
rozszerzające do Wisła 64, które miało umożliwić bezpośrednią
wymianę danych między dwiema maszynami na znaczne odległości.
Roboczo zostało nazwane Piast, ponieważ miało połączyć maszyny
kalkulujące, niczym pierwsi królowie Korony Polskiej połączyli
nadwiślańskie plemiona.
Pierwsze prezentacje okazały się nie lada sukcesem. Kierownictwo
zaczęło naciskać, żeby możliwe było podłączenie wielu takich urządzeń
do jednej maszyny Wisła 64, dzięki czemu mogłaby się ona
komunikować w wiele stron naraz. Ponadto dodano wymaganie, aby
komunikacja działała również w trybie pośrednika(coś jak zabawa w
"podaj dalej"). Przekroczeniem Rubikonu była jednak decyzja na
zaadaptowanie produktu pod zagraniczne maszyny liczące. Podjęta ona
została, ponieważ dyrekcja nawiązała współpracę biznesową z
Dortmundzką spółką NWRW(Nordhein-Westfallen Rechenwerke
GmbH) i podpisała kontrakt na ich autorską technologię o nazwie
WindRad. Reklamowana była jako rewolucja w dziedzinie
programowania obliczeniowego, ponieważ miała umożliwić uniwersalny
zapis programu, który będzie możliwy do translacji na dowolny system
obliczeniowy. Z założenia miało to odciążyć od projektowania
programu na bazie konkretnej maszyny matematycznej, której niuanse
wymagały podejmowania wielu decyzji dotyczących mało istotnych
szczegółów.
Symbolika marki opisywała to jako podmuch wiatru na młyn
napędzający inżynierię superwydajnych systemów obliczeniowego
przetwarzania danych. My jednak zrobiliśmy sobie z niej obiekt żartów
o tym, że wyraża ona podświadomą tęsknotę zarządu NWRW za
reżimowym ustrojem, który ich naród sam obalił pokolenie wcześniej.
PolACME utworzyło specjalny dział, który miał pomóc NWRW w
opracowaniu translatorów WindRad na maszyny Wisła 64 oraz Orzeł 8.
Już po kilku miesiącach otrzymaliśmy prototypy, na bazie których
mieliśmy opracowywać WindRadową wersję programu urządzenia
komunikacyjnego Piast.
Czuć było w tym pewną inercję. Dokumentacja techniczna WindRada
była opasła i poruszanie się po niej wymagało nieco wprawy. Pobieżne
zapamiętanie spisu treści nieco przyspieszało ten proces. Sama treść
dokumentacji dawała po sobie znać, że została przetłumaczona przez
kogoś, kto średnio rozumiał język polski.
Wszystkie słowa kluczowe WindRada pochodziły z niemieckiego. Nie
miałem z tym większego problemu, mój ojciec był z pochodzenia
Niemcem. Ożenił się jednak z Polką i z tego powodu w Polsce się
osiedlił. Pracował jako pianista w regionalnej filharmonii. Przekonywał
często matkę, że nadał mi imię na cześć Chopina, jednak pod jej
nieobecność opowiadał mi o chwale Cesarstwa Pruskiego oraz
wielkości Świętego Cesarstwa Rzymskiego za panowania Barbarossy.
Nie omieszkał przy tym wtrącać różne szlacheckie aforyzmy.
"Pamiętaj synu, najpotężniejsza armia nie uczyni z ciebie władcy, jeżeli
kupisz ją za cenę suwerenności."
Nie trafiało to wtedy do mnie, bo byliśmy prostą rodziną z miasta.
Najpotężniejszym żołnierzem naszego domu była matka uzbrojona w
tłuczek do kotletów. I bez tego była w stanie zrobić sobie z ojca
podnóżek. Do końca życia nawijał o Czystości i tym, że liczy się tylko
pismo i każdy ma takie same prawo jego interpretacji. Od czasu do
czasu przebąkiwał coś o rzymskich uzurpatorach, którzy są diabłami
świecącymi w ciemnościach. Jednak dla ukochanej Gracji ugiął się i
ochrzcił jedynego syna po katolicku. Może to właśnie ta rozdwojona
wizja na moje życie zaszczepiła we mnie jakiś dualizm, który
uniemożliwił poważne podejście do obu z rodzimych wyznań. Poza tym
nie potrafił wytrzymać choćby jednego przyjęcia bez tyrady o tym, jaki
to ród von Nischwitzów nie był wpływowy, mimo że nie utrwalił się
szczególnie na kartach historii. Wobec czego był prześmiewczo
nazywany za plecami baronem Terenzem.
Ja również nie podzielałem tej sympatii, więc irytowało mnie gdy
koledzy z pracy przychodzili do mnie z pytaniami o wpływ niuansów
języka niemieckiego na WindRad, bo nie znałem go wcale aż tak
dobrze.
Notacja, którą zapewnił nam WindRad od czasu do czasu pokrywała się
ze schematami powtarzających się przypadków projektu Piast. Te
schematy dokumentowaliśmy w formie biblioteki zawierającej schematy
list instrukcji, które były opisane pod kątem problemu, jaki
rozwiązywały. Notacja WindRada przypominała nieco notatki boczne
tych dokumentów z tym, że nie były one tylko pustymi słowami, bo
translator weryfikował spójność programu pod kątem użytych notacji.
Niestety po przepisaniu bazowej części komunikacyjnej Piasta do
WindRad maksymalna przepustowość danych spadła o niemal 30
procent. Wzbudziło to alarm u kierownictwa działu, bo kampania
marketingowa zaczęła już obiecywać pewne liczby, a teraz okazały się
one nieosiągalne.
Dlatego PolACME wystąpiło z podaniem do NWRW, aby zapewnili
nam szkolenie w kwestii efektywniejszego używania WindRada.
Producent WindRada zebrał wywiad od spółek używających ich
produktu i spisał opasłe kompendium pełne różnorodnych technik i
zasad użycia. Kopia woluminów trafiła do naszego działu i
kierownictwo wymagało od nas ich studiowania i stosowania się do ich
przykazu. Zorganizowanie sobie na to czasu oraz systemu
współdzielenia dostępu do ksiąg pozostało kwestią przemilczaną.
Napięcie wzrosło jeszcze bardziej, gdy dział rozwijający Drzwi zaczął
adaptować konkurencyjną technologię. Ich kierownictwo zdecydowało
się na wschodnie rozwiązanie o nazwie Szczipcy(ros. щипцы).
Reklamowana była jako coś co pozwoli złapać rozgrzaną do bieli ideę
programu i kuć ją póki gorąca. Poza tym miało się to kojarzyć z
pewnym i bezpiecznym chwytem.
W rzeczywistości koledzy, którzy zaczęli je używać byli głównie
uszczypliwi wobec wszystkich dookoła. Zaczęli nam suszyć głowy, że
rozwiązania, które piszemy pod WindRad są podatne na awarie i
manipulacje z zewnątrz, więc przezwaliśmy ich kultem czerwonego
kraba.
Kuriozum osiągnęło punkt krytyczny w momencie, w którym doszło
do sprzeczki między mną, a kolegą z działu o to jaki zapis w
WindRadzie użyć. Popchnęło mnie to do przeprowadzenia iście
szaleńczego badania, którym chciałem wykazać swoją rację, a w
konsekwencji doprowadziło do niebagatelnych wniosków.
Przedstawię to za pomocą zapisów moich kontemplacji, które ukażą je
z perspektywy bliższej temu, co myślałem i czułem w ciągu wydarzeń.
CAPUT II
DŻIHAD USPRAWIEDLIWIA ISKRĘ, OD KTÓREJ ŚWIAT STAJE W OGNIU
Pracuję nad realizacją protokołu ustanawiania
łączności między dwoma modułami Piast.
Zgodnie z wytycznymi każda próba ma
odłożyć w dzienniku zdarzeń komunikat
adekwatny do odpowiedzi otrzymanej z drugiej
strony.
W dokumentacji projektowej, którą
otrzymałem mam wylistowane jaki komunikat
powinien trafić do dziennika w odpowiedzi na dany kod zwrotny.
Tak się składa, że widziałem już dosyć podobne przypadki w projekcie
Piast i zazwyczaj były one realizowane za pomocą struktury sterującej
FLIPPER.
Dopiero niedawno dotarło do mnie, czemu została ona nazwana jak
bilard elektryczny. Dopasowanie pod wartość obok słowa kluczowego
REIN powoduje wykonanie instrukcji nawet jeżeli rozdzielają je inne
REIN-y. Wiersz RAUS jest jak przeszkoda, która kieruje kulkę
przepływu sterowania w bok.
DATEN kod ZAHL
FLIPPER kod
REIN 0
TUN wpiszDoDziennika("abc")
RAUS
REIN 1
TUN wpiszDoDziennika("def")
RAUS
REPPILF
Przypadki użycia FLIPPER-a z którymi się spotykałem bywały nieeleganckie. Bardzo raziły mnie zapisy w których kilkanaście REIN ów stanowi etykietę skoku dla tej samej operacji przypisania wartości do symbolu zmiennej lub wypisania komunikatu.
Przeglądając dokumentację WindRad szybko wpadłem, że da się to w bardzo prosty sposób wyrazić za pomocą agregującego typu danych ZIPP. Mogę potraktować kody zwrotne jako klucze i komunikaty jako sparowane wartości. Więc konkretny typ muszę zbudować z typów podstawowych ZAHL oraz KETTE.
DATEN komunikatySynchronizacji ZIPP[ZAHL->KETTE]
ANFANG
0 -> "abc"
ENDE
DATEN komunikatDziennika KETTE
komunikatDziennika := komunikatySynchronizacji[kod]
Wyszedłem z tą propozycją rozwiązania do reszty działu i otrzymałem
komentarz od Michała, aby nie używać ZIPP-a. Twierdzi on, że jego
działanie będzie zbyt wolne ze względu na dodatkowe alokacje pamięci
za pośrednictwem systemu. Jak na ironię zasugerował, aby napisać to z
użyciem FLIPPER-a.
Po paru wymianach argumentów w kwestii estetyki obu rozwiązań
uznałem, że bardziej korzystnym dla projektu będzie zaakceptowanie
jego sugestii niż twarda obrona swojego stanowiska, która skutkowałaby
przeciąganiem pracy. Poza tym muszę mu nieco oddać honoru, bo
jednak jest starszy stanowiskiem.
Przystałem na opakowanie FLIPPERA w podprogram. Dzięki czemu
operacje związane z każdym REIN-em nie polegały na przypisywaniu
wartości do symbolu komunikatu i wychodzeniu ze struktury
sterowania słowem kluczowym RAUS, tylko zwracaniu wartości z
podprogramu. Ta wartość może zostać przypisana do symbolu
komunikatu przez wywołanie tego podprogramu.
UNTERPROGRAMM KETTE dopasujKomunikatDoKoduZwrotnego
kodZwrotny ZAHL
STARTE
FLIPPER kodZwrotny
REIN 0 ZUR := "abc"
REIN 2 ZUR := "def"
REPPILF
ENDE
DATEN komunikat KETTE
TUN dopasujKomunikatDoKoduZwrotnego(kod) =: komunikat
***
Mija kilka dni odkąd skończyłem prace nad wątkiem ustanawiania
łączności. Temat dopasowywania komunikatu do kodu zwrotnego nadal
nie jest w stanie ujść z mojej głowy.
Nawet początkujący programista powinien być w stanie z miejsca
zapisać do niego rozwiązanie. Chociaż początkujący pewnie nie
zreflektowałby się nad innymi możliwościami, a da się to zrobić na kilka
sposobów o podobnym poziomie elaboracji.
Jakoś nie daje mi spokoju, że brakuje nam konsensusu co do tego, który
sposób jest najlepszy. Wydawało mi się, że mam już dostatecznie dużo
doświadczenia, żeby podejść do tego tematu bezbłędnie, a mimo to
starszy kolega okazał się być za rozwiązaniem, które sam wcześniej w
uzasadniony sposób odrzuciłem.
Problem jest tak prosty, że dyskusja wokół niego wydaje się być stratą
czasu. Skoro problem jest trywialny, to czemu WindRad nie dostarcza
czegoś, co rozwiązuje problem w sposób co do którego eksperci byliby
zgodni?
Czyżby nie dotarła jeszcze do mnie jakaś tajemnica przez którą mój
osąd jest gorszy? Może jestem jeszcze za mało obeznany z
dokumentacją techniczną i wolumenami WindRada.
Zajrzałem do wolumenów w sekcje dotyczące FLIPPER oraz ZIPP.
Księgi zalecają użycie FLIPPER postulując, że jest przetłumaczalny na
tablicę skoków o szybkim czasie dostępu. Z kolei ostrzega, że ZIPP ma
nieco długie czasy dostępu ze względu na rozsianie po pamięci
operacyjnej. Czyli to uzasadnienie odrzucenia ZIPP nie było wzięte z
powietrza. Nie dziwię się, bo Michał nie ma tendencji do obrzucania
nas banialukami.
Z tym, że te zapisy w księgach nie zestawiają bezpośrednio obu notacji
twarzą w twarz. Wobec czego w mojej głowie rodzi się pytanie
względem czego są one szybkie lub wolne?
Moim być albo nie być jest wprowadzenie nieco sceptycyzmu. Nie tak
dużo, żebyśmy zaczęli kwestionować kulistość Ziemi, ale na tyle dużo,
żeby zadać sobie pytanie "Skąd pewność, że dane rozwiązanie w
WindRad będzie lepsze od innego?".
Przecież mamy narzędzia, które pozwolą nam zebrać dane na ten temat,
a metoda naukowa pomoże nam znaleźć odpowiedź. Nie powinienem
tak po prostu pozwalać na to, żeby ucierały się u nas schematy, które
oparte są na przypuszczeniach. Być może za pierwszym razem schemat
doboru poprzez FLIPPER został utworzony po najmniejszej linii
oporu, za drugim razem skopiowany z pierwszego, a przy trzecim razie
był już złotym wzorcem, którego złamanie stanowi podstawę do bicia
na alarm.
Nie wiem jeszcze przeciwko komu albo czemu walczę. Czy jest to walka
z kolegą o to kto ma rację czy może walka przeciwko kolektywnej
głupocie ludzi z mojego zawodu? Walka z jednym nieprzemyślanym
przypadkiem czy wręcz całym WindRadem, którego wprowadzenie
wydaje się wnosić większe problemy niż te, które mieliśmy dotychczas?
W każdym razie jest to walka warta podjęcia, bez względu na to jakie
reperkusje mogą na mnie spaść.
CAPUT III
STATYSTYCZNIE ISTOTNY POMIAR WŁASNEGO CIENIA
Przed rozpoczęciem pomiarów dodam inne
możliwe rozwiązania problemu, żeby nie
przypominało to pojedynku z fantasmagorii
typu Mozart kontra Bach.
Pierwszym co przychodzi mi do głowy jest
struktura WENN. Podobnie jak FLIPPER jest
to struktura wyboru.
W tym przypadku jednak każdy warunek
będzie polegał na jawnym porównaniu
symbolu kodu zwrotnego do wartości całkowitoliczbowej.
Sprawdzenie reszty kodów zwrotnych mogę zawrzeć w łańcuchu
DOCH WENN.
Struktura WENN ma tę zaletę, że nie wymaga jawnego wyskakiwania z
bloków operacji dla spełnionego warunku tak, jak robi to RAUS we
FLIPPER-ach. Jest to całkiem logiczne, jeżeli o każdym DOCH
WENN pomyśleć jako o WENN-ie w sekwencji po DOCH.
DATEN kodZwrotny ZAHL
DATEN komunikatDziennika KETTE
WENN kodZwrotny = 0
komunikatDziennika := "abc"
DOCH WENN kodZwrotny = 2
komunikatDziennika := "def"
NNEW
Kolejnym, nieco mniej oczywistym rozwiązaniem będzie FOLGE. Jako, że kod zwrotny jest wartością całkowitoliczbową mogę użyć go jako indeks dolny ciągu i podobnie jak w ZIPP zawrzeć odpowiadające im wartości komunikatów.
DATEN komunikatyDziennika_10 FOLGE_KETTE
ANFANG
"abc"
"def"
ENDE
DATEN komunikat KETTE
DATEN kodZwrotny ZAHL
kodZwrotny := 1
komunikat := komunikatyDziennika_kodZwrotny
***
Uzyskałem dostęp po godzinach do stanowiska Wisła 64 działającego pod kontrolą systemu Drzwi. Przygotowałem program pomiarowy. Polega on na pomiarze czasu dopasowania elementu dla wejścia o wartości 0. Dla każdej wersji dopasowanie jest wykonywane 1000 razy, więc wynik dzielony jest przez tę wartość w celu uzyskania średniego czasu.
Wynik pierwszych pomiarów dla wersji z ZIPP jest niemal rząd wielkości większy niż pozostałe. Różnice pomiędzy pozostałymi są marginalne, ale FLIPPER wydaje się mieć minimalną przewagę.
Dodałem próbę kontrolną, żeby zobaczyć ile czasu zajmują same
operacje związane z metoda pomiarową. Iteracja oraz przepisywanie
wartości między symbolami również mają wkład w czas wykonania.
Wygląda na to, że względem wersji innych niż ZIPP czas wykonania
próby kontrolnej wynosi nieco ponad 50%.
Tym razem wersja oparta na ciągu miała najkrótszy średni czas
wykonania. Liczyłem na to, że uśrednianie anuluje wzajemnie
jakiekolwiek fluktuacje czasów pojedynczego wykonania. Spróbuję
zmierzyć czas pojedynczego dopasowania.
Nie jest wesoło, 99% pomiarów to 0 albo 100 mikrosekund. Za to
około jeden na sto pomiarów osiąga jakieś niebotyczne wartości typu 1
albo 10 milisekund.
Domyślam się, że to jakieś ograniczenia kwantyzacji zegara. Dziwne, bo
opis podprogramu ZEIT w dokumentacji technicznej WindRad nie
wspomina o niczym takim. Może to ograniczenie wynika z samej
Wisły?
Bardziej niepokoją mnie jednak te pomiary odstające. Najpewniej
system Drzwi jest w stanie wywłaszczyć wykonanie od programu, który
aktualnie wykonuje się w jego środowisku. Trochę to dziwne, bo nie
pomyślałbym, że może się to stać nawet przy tak małej skali operacji.
Pewnie jakaś czarna magia Szczipców, cholerny kult czerwonego kraba!
A może to jednak jakieś zachowanie Wisły nad którym wcześniej nie
miałem okazji się pochylić?
Próbowałem przez chwilę estymować wartości pomiędzy kwantyzacją
zakładając, że podlegają one stałemu rozkładowi dwuwartościowemu.
Trochę jak szacowanie długości kijka poprzez rzucanie go na linie
rozłożone równo w odległościach większych niż sam kijek. Ciekawe co
powiedziałby na to Mistrz Ulam?
Wyniki nie są równe pomiędzy eksperymentami. Podejrzewam, że sam
podprogram ZEIT nie dostarcza stabilnej wartości. Jakby nie było, nie
może on mieć zerowego czasu wykonania i wartość, którą zwraca,
będzie miała różną fazę względem pulsu zegara.
Że też podjąłem się tak zawiłego pomiaru. Chociaż to nie pierwszy raz,
bo już kilka razy w pracy słyszałem dowcipy typu "Daj to Niszwicowi.
On prędzej zrobi dziurę głową w ścianie, niż ośmieli się stwierdzić, że
coś jest niemożliwe, zanim tego nie spróbuje.".
Wróciłem do pomiarów obejmujących wielokrotną operację
dopasowania. Zacząłem od dobrania sensownej liczby operacji w
ramach pomiaru czasowego. Zakładając, że kwantyzacja i błędy
podprogramu ZEIT mogą wnieść +- 200 mikrosekund błędu do
pomiaru to 2000 krotna operacja powinna przełożyć się na +-100
nanosekund błędu w średnim wyniku.
Tym razem nie liczyłem średniej tylko zebrałem serię 1000 pomiarów
dla każdej wersji i przeanalizowałem.
Niestety pomiary nie wykazują żadnego pewnego rozkładu. Dla każdej
z serii widać jakąś modę a nawet kilka, ale zawsze ciągnie się za nimi
jakiś ogonek pojedynczych bardzo dużych wartości.
Udało mi się zniwelować duże wartości. Posortowałem serię i
znalazłem w niej punkt za którym wartości rosną w znacznie szybszym
tempie. Odrzuciłem wszystkie wartości za tym punktem i otrzymałem
oczyszczoną serię w której mody stanowią środek wartości.
Jednak wciąż nie układają się w nic wyglądającego sensownie oraz
replikowalnego pomiędzy eksperymentami. Myślałem, że na mocy
centralnego twierdzenia granicznego osiągnę już zbieżność średniej.
Poeksperymentowałem nieco z liczbą wykonań operacji wewnątrz
pomiaru. Dopiero teraz zdałem sobie sprawę, że błędy wynikające z
wywłaszczania wykonania jedynie się akumulują, bo jest to opóźnienie,
które nie może przyjąć wartości ujemnych. Zatem zwiększanie liczby
operacji będzie jedynie zwiększać błąd.
Dlatego też nie jestem w stanie dokonać pomiaru, który doprowadzi
mnie do wartości czasu wykonania samej operacji dopasowania. Zresztą
nie ma w tym większego sensu, bo każde wykonanie jest obarczone
ryzykiem opóźnienia. Więc wynik, który oddaje średnią wartość tego
opóźnienia, również niesie nieco wartościowej informacji.
Wygląda na to, że wersja oparta na FLIPPER daje najmniejszą wartość oczekiwaną, wersja WENN jest nieco wyższa, ale uwzględniając odchylenie standardowe nakładają się w ok 40%, co oznacza, że ta dominacja nie jest pewna. Z kolei wersja oparta na FOLGE jakkolwiek ma wartość oczekiwaną jedynie 10% większą, to nakłada się w obszarze dalszym niż 3 sigmy, więc jest praktycznie pewne iż będzie powolniejsza. Wartość oczekiwana przy wersji ZIPP pozostaje ok 8 razy większa niż pozostałe trzy.
Nie jestem w stanie wyciągnąć z tego żadnych pewnych wniosków dla pojedynczego dopasowania. Dosłownie powinienem powiedzieć, że wersja FLIPPER jest najszybsza w akumulacji czasu 2000 wykonań. Aczkolwiek koniec końców to i tak są tylko hipotetyczne wnioski z niezbyt formalnych eksperymentów. Nie wiem do końca jak udowodnić, że te wyniki rzeczywiście wynikają z doboru wersji zapisu. Być może wciąż główną rolę gra tu losowość.
Cóż za bezsens, tyle czasu i dni spędzone na eksperymentach, które nie udowodniły, że kolega miał rację, ale też nie wykazały wprost, żebym to ja się mylił. Wszystko rozbiło się o precyzje pomiaru, losowość i niepewności.
Chyba jedyna pewna mądrość jaką mogę z tego wyciągnąć, to że nie można powiedzieć nic pewnego na temat czasu wykonania jakiejkolwiek operacji dostarczanej przez WindRad. To jest taki miks charakterystyk samej operacji oraz systemu obliczeniowego na którym jest wykonywana, z którego praktycznie nie da się odseparować co od czego zależy. Gdybym wykonał te eksperymenty na Orzeł 8 to być może porządek czasów względem wersji byłby całkowicie inny. Dotychczas wydawało mi się, że dostrajanie programu w oparciu o takie wyniki jest wyższym poziomem jego okiełznania, a jednak okazuje się to być tańczeniem do rytmu, który zagra konkretna maszyna kalkulująca.
Z ciekawości sprawdziłem jeszcze jak operacje dopasowania mają się do
samej operacji wpisu do dziennika. I okazało się to rewelacją, ponieważ
średni czas serii operacji zapisu jest ok. 1000 razy większy niż czas
operacji dopasowania.
To właściwie całkowicie zmienia punkt widzenia. Zamiast skupiać się na
dyskusji o to, która wersja dopasowania komunikatu do kodu
zwrotnego jest szybsza powinienem był przesunąć ją w stronę kwestii
wpływu tego wyboru na szerszy kontekst programu. Przecież to samo
wpisanie do dziennika jest głównym źródłem kosztów czasu
wykonania.
Jaki jest sens, żebyśmy sami fatygowali się w oszczędzaniu grosików
skoro wytyczne zmuszają nas do trwonienia złotówek? Źdźbło, które
złamie wielbłądzi grzbiet padnie na kufry pełne kilogramów operacji,
których dodanie do programu było zlecone z góry. Nikt nie pytał nas o
zdanie jak wielkie są to koszty w porównaniu do wartości jaką dodają
do produktu.
Więc w swoim zdaniu Michał się nie mylił, ale użył złego argumentu.
Zapis ZIPP jest powolniejszy, ale z perspektywy użytkownika
końcowego najpewniej nie byłoby to odczuwalne.
W takim razie to ja popełniłem błąd, bo dałem się wciągnąć w
wartościowanie swojego rozwiązania wyłącznie pod kątem czasu
wykonania jaki wprowadza.
Dlatego chcąc osądzić, które z nich jest lepsze, więcej zyskam, jeżeli
przeanalizuję te zapisy w sposób całkowicie oderwany od
jakiegokolwiek kontekstu w jakim mogą zostać osadzone.
CAPUT IV
ANALIZA UKŁADU ZAMKNIĘTEGO Z
PERSPEKTYWY WIĘŹNIA
Przeanalizuję użyte notacje WindRada w sposób całkowicie oderwany zarówno od systemów obliczeniowych na które mogą zostać przetłumaczone, jak i problemów względem których mogą zostać użyte.
Zaczynając od nieszczęsnego FLIPPER-a,
który jest kością niezgody między mną a
Michałem i może wręcz resztą działu.
Jako, że jest to struktura sterująca, to nie da się jej wprost zamknąć w
żadnym symbolu. Można co najwyżej opakować ją w podprogram tak,
jak zostało uczynione w ramach kompromisu. Zatem jej reużywalność
wymaga dodatkowych notacji, ewentualnie skopiowania. To, czy
skopiowanie jest dobre czy złe, zależy od tego, czy taka kopia
reprezentuje coś niezależnego. Jeżeli nie, to będzie się miało do
czynienia z powielaniem definicji, których zmiany będą wymagały
synchronizacji w wielu miejscach.
Możliwe wartości wejściowe ograniczają się do liczb całkowitych. Także
struktura nadaje się do użycia tak długo, jak wejście przyjmuje postać
liczby całkowitej. Wyjątkowo FLIPPER ma jeszcze tę zaletę, że nie
pozwala na zdublowanie wartości przy REIN-ie. Możliwość grupowania
wielu takich REIN-ów uważam za wadę, bo widywałem już przypadki
użycia w których takie zgrupowanie potrafiło liczyć kilkanaście wartości
co wymaga sporego skakania oczami podczas czytania. Jednak jak
wyszło w praniu, reszta zespołu nie podziela tego poczucia moralnego.
Możliwość umiejscowienia dowolnej sekwencji instrukcji pod etykietami
daje swobodę strukturyzacji. Wykorzystuje się tego potencjał w
przypadkach, kiedy działania są zróżnicowane dla każdego wejścia.
Jedyne co nie daje mi spokoju to nieszczęsne RAUS. Dokumentacja
techniczna nie tłumaczy się z tego, dlaczego wejście w jakikolwiek
REIN będzie wykonywać wszystkie instrukcje poniżej, nawet
rozdzielone innymi REIN-ami. Zgaduję, że pozwala to na opracowanie
piramidy wartości wejściowych, wedle której bazowe wartości skutkują
wykonaniem wszystkich instrukcji, z kolei każda kolejna grupa jest ich
zawężeniem. Niestety nie spotkałem się w praktyce z takim użyciem,
więc może taka potrzeba jest tylko wymysłem projektantów WindRada.
O strukturze WENN można powiedzieć dokładnie to samo w kwestii
reużywalności oraz elastyczności struktury wewnętrznej. Przede
wszystkim WENN daje swobodę warunków wejściowych, bo nie
ogranicza się to tylko do porównania do konkretnej wartości
całkowitoliczbowej. Z mojej strony plus za to, że sekwencje instrukcji
do wykonania muszą być odseparowane. Większość przypadków użycia
FLIPPER-a jest podatna na błąd ze strony programisty w postaci
pominięcia RAUS, co skutkuje innym programem niż zamierzano. Z
kolei WENN pozwala na błąd w postaci zdublowania warunku
wejściowego, który nigdy nie zostanie przetworzony. Nie widzę jednak
przeszkód, żeby program tłumaczący był w stanie zwrócić na to uwagę.
No może poza zwiększonym czasem przetwarzania.
Ostatni DOCH pozwala na zagregowanie pozostałych przypadków.
Można to wykorzystać do zdefiniowania działania w przypadku wejścia
w stan nieokreślony.
Jakkolwiek uznałem ZIPP za najlepsze rozwiązanie dla przypadku
dopasowania, to nie jest ono bez wad. Zamknięcie w symbolu daje
reużywalność, a inicjalizacja par klucz-wartość jest zwięzła i czytelna.
Jednak w samej inicjalizacji jest pewien szkopuł, a mianowicie podobnie
jak WENN nic nie stoi na przeszkodzie, aby w sekwencji wartości ten
sam klucz pojawił się po raz kolejny. Podobnie jak z warunkami
WENN zdublowany klucz nie przekłada się na żaden efekt, ponieważ
wartość przy nim nie nadpisuje wcześniejszej.
Poza tym ZIPP nie umożliwia zdefiniowania reakcji na nieokreślony
klucz. W przypadku próby uzyskania wartości dla nieokreślonego
klucza zwrócona zostaje zeropodobna wartość typu wyjściowego. W
przypadku KETTE jest to pusty łańcuch znaków. Dlatego też
rozpoznanie takiej sytuacji wymaga dodatkowych instrukcji
warunkowych w programie.
Typ danych FOLGE nieco przypomina nieślubne dziecko ZIPP-a oraz
FLIPPER-a. Jednocześnie jest reużywalny pod postacią symbolu, ale
ogranicza wartości wejściowe do liczb całkowitych.
Inicjalizacja również ma swoją podatność na błędy. Od programisty
wymaga się zliczania już określonych wartości wyjściowych w celu
określenia jakiej wartości wejściowej odpowiadają. Poza tym inicjalizacja
wymaga przejścia wszystkich wartości pomiędzy, więc jakiekolwiek luki
w wartościach wejścia będą wymagać uzupełnienia czymś oznaczającym
wejście nieokreślone.
Najbardziej nieszczęsny jest w tym Grauzone związany z odgórnym
określeniem liczby elementów. Odniesienie się do indeksu dolnego poza
rozmiarem skutkuje nie wiadomo czym. Może to być katastrofa, a może
to być jedno wielkie nic.
Pojęcie "moralności" jakim posłużyłem się w tym kontekście to tylko
indywidualne poczucie estetyki oraz subiektywna ocena kroków pisania
programu w których najłatwiej jest o pomyłkę i napisanie czegoś
niezgodnego z intencjami. Nie ma w tym nic jednoznacznie
rozdzielającego dobro od zła. Jak również nic obiektywnie
wzbudzającego zachwyt albo obrzydzenie.
Jakkolwiek starałbym się przeanalizować te notacje WindRada w sposób
całkowicie oderwany zarówno od konkretnego systemu, jak i od
problemu, to nie jestem w stanie powiedzieć nic konkluzywnego bez
umieszczania tego chociażby w kontekście konkretnego problemu,
który rozwiązuję. Ba, bez poszerzenia tego kontekstu na całość
projektu ten osąd również jest miałki. Problem jest prosty, ale trywialny
może stać się dopiero wraz z ustaleniem pełnego kontekstu w którym
jest osadzony. Tylko, że projekty same w sobie nie są czymś stałym, bo
ich wymagania mogą ulegać ciągłej zmianie. Tylko ktoś kto zna projekt
na wskroś oraz jego przyszłość jest w stanie dokonać optymalnego
wyboru.
Chociażby wątek danych wejściowych: jakkolwiek ustalone zostało, że kody zwrotne przyjmą postać liczb całkowitych to nie można w pełni wykluczyć przyszłości w której zastąpione zostają one chociażby mnemonikami tekstowymi. W takim przypadku zapisy z użyciem FLIPPER oraz FOLGE przestają być bezpośrednio realizowalne. Ich wcześniejsze użycie wydaje się być założeniem ze strony programisty, że taka sytuacja jest wielce nieprawdopodobna.
Można również wyobrazić sobie sytuację w której zaistnieje potrzeba wyniesienia definicji komunikatów poza program, do danych konfiguracyjnych ładowanych w czasie jego wykonania z pamięci pomocniczej. W takim przypadku struktury sterujące tj. FLIPPER i WENN uniemożliwiają wprowadzenie zmian bez zdekonstruowania zapisu programu.
Wybór między strukturami sterującymi, a zawarciem dopasowań w
symbolach również podlega pewnej spekulacji. Otóż zgodnie z
wytycznymi każde otrzymanie kodu zwrotnego powinno skutkować
wpisem komunikatu do dziennika. Co w momencie w którym dla
pewnego kodu zwrotnego pojawi się specjalna wytyczna? Wtedy to
struktury sterujące umożliwią najprostsze wprowadzenie jej do
programu. Użycie ZIPP albo FOLGE wymagałoby chociażby
ustanowienia osobnego bloku warunkowego.
Jednak użycie struktur sterujących dla kogoś innego może wydawać się
nieco sugerować, że programista nie jest w stanie dostrzec ogólnego
zachowania dla każdego przypadku, które polega na ustaleniu wartości
komunikatu dziennika. Struktura WENN jest w tej kwestii jeszcze
gorsza, bo dla każdego przypadku wejściowego powtarza operację
porównywania kodu zwrotnego.
Z jednej strony widzę, że notacje WindRada mają w sobie pewien
ukryty język projektowy. Nadają strukturę, która między słowami niesie
różne intencje autora. Jednak z drugiej strony można ich używać jako
pierwszy lepszy środek do celu. Nie przejmować się formą i stawiać na
treść, która skutkuje oczekiwanym zachowaniem.
Jeden programista może kierować się maksymą, żeby robić coś raz, a
dobrze i projektować swoje rozwiązania mając na uwadze możliwy krok
naprzód. Drugi z kolei, że wystarczy rozwiązywać problem, który jest tu
i teraz, a nie jakiekolwiek możliwe przyszłe problemy.
Obie postawy są pewnym wyborem, którego w zapisie programu
praktycznie nikt jawnie nie oddaje. One nawet nie stanowią
fundamentalnych pryncypiów, bo ta sama osoba może używać ich
naprzemiennie w zależności od sytuacji i nastroju.
Temat prowadzi do czegoś przypominającego płot Chestertona, kiedy
mając do czynienia z cudzym rozwiązaniem lub czymś, co zapisało się
w dalekiej przeszłości trzeba się zastanowić, czy ustanowiona struktura i
wynikające z niej ograniczenia mają znaczenie, czy są przypadkowe.
Łopatologiczny zapis sprawia wrażenie bycia dziełem łopatologicznie
myślącego umysłu. W związku z czym używanie mniej wyrafinowanych
środków wyrazu może okazać się strzałem w stopę, jeżeli ktoś wybiera
je wyłącznie dla efektywności i chce tym sprawić wrażenie geniuszu.
Poza tym czemu niby jedna składnia miałaby być w każdym przypadku
lepsza od drugiej składni? Takie porównania bez kontekstu to jak
rozstrzyganie wyższości między jabłkiem, a pomarańczą. Można
kierować się konwencjami na temat parowania smaków, ale wybitny szef
kuchni potrafi je łamać tak, jak wybitny muzyk potrafi wplatać
dysharmoniczne interwały w melodię.
Jakby nie było to korekta błędu, a nawet całkowita zmiana notacji części
programu i tak jest drobnostką w skali takich problemów jak to
dopasowywanie komunikatów do kodów zwrotnych i inne, które
mieszczą się w raptem parunastu linijkach tekstu obejmowalnych
wzrokiem.
O wiele większym problemem może być zdiagnozowanie błędu jeżeli
przeszedł on już wszystkie etapy procesu produkcji. Jego konsekwencje
mogą przyjąć praktycznie wszystkie wartości w skali od
niezauważalnych do śmierci ludzkiej i zależy to przede wszystkim od
tego, do czego program jest używany.
Stara mądrość mówi, że ryba psuje się od głowy, zatem to w gestii
kierownictwa projektu powinno leżeć ustanowienie kontroli jakości oraz
barier linii technologicznej, które mają zapobiec wszelkim incydentom
prowadzącym do odszkodowań. Zwracanie uwagi, że wydajność i
odporność programu należy zapewniać działaniami oddolnymi
programistów trąci propagandą, której celem jest spychalstwo
odpowiedzialności odgórnej.
Po raz kolejny nie jestem w stanie dojść do porozumienia we własnym
dialogu wewnętrznym względem rzekomo prostego problemu. Wobec
czego zastanawiam się w jaki sposób NWRW udało się zebrać tyle
porad i technik dotyczących WindRada samego w sobie? Czyżby tamci
eksperci posiadali jakiś żelazny zbiór osobistych pryncypiów o których
dotychczas nie słyszałem, a może po prostu nie posiadają zdolności
refleksji i tarzają się w uprzedzeniach? Być może wręcz wpatrują się oni
tak długo w coś, czego nie da się racjonalnie ocenić, że zaczynają
prawić cokolwiek na bazie halucynacji wynikających z braku stymulacji
mózgowej.
Po wdrożeniu WindRada co chwilę pojawia się jakiś głos, że nawet jak
rozwiązanie elementarnego problemu jest poprawne pod kątem
oczekiwanego efektu, to jest złe, bo jest zapisane nieładnie albo
twierdzi się, że w danej notacji będzie wykonywać się zbyt powoli lub w
sposób podatny na awarie. Co i rusz jest przerzucanie się zapisami
"lepszymi" lub cytowaniami z ksiąg WindRada, że tak się robić nie
powinno.
Nie pamiętam, żeby w czasach w których programowaliśmy
bezpośrednio pod model Wisły 64 taka niezgoda w ogóle miała miejsce.
Niemal każde rozwiązanie było dla nas zadowalające, a jeśli już
zwracaliśmy sobie na coś uwagę, to na operacje nadmiarowe.
Przestało mieć pierwszorzędne znaczenie, żeby wykazać, iż program w
danym zapisie będzie dostarczał oczekiwany efekt. Teraz głównie patrzy
się na to, czy jest on zgodny z etykietą, a obejść to można jedynie
sprytną retoryką lub nieczystymi zagrywkami.
Może uda mi się nieco zrozumieć to, co zaobserwowałem przy
pomiarach jak zbadam to, od czego odepchnął mnie WindRad, a
mianowicie program z perspektywy Wisły 64. Te rozmyślania oderwane
od świata zewnętrznego sprawiają, że powoli sam zaczynam odchodzić
od zmysłów.
CAPUT V
ZWIEDZANIE RAJU UTRACONEGO
Tak się składa, że program tłumaczący
WindRad umożliwia przetworzenie do
poziomu notacji pomocniczej Wisła 64, która
umożliwia zapis programu w postaci
mnemoników instrukcji.
Nieco czasu minęło odkąd ostatni raz pisałem
program w tej notacji, więc muszę odgrzebać
jakąś dokumentację użytkownika. Mam
nadzieję, że analiza instrukcji Wisły do jakich
tłumaczony jest WindRad rozjaśni nieco wyniki otrzymane w ramach
pomiarów oraz doprowadzi do nieco bardziej konkluzywnych
wniosków w kwestii sporu o to jak najlepiej napisać dopasowywanie
komunikatu do kodu zwrotnego.
Żeby nie utrudniać sobie życia nieco zmienię badany program i zamiast
dopasowywać wartość tekstową typu KETTE będę dopasowywał
wartość liczbową z przecinkiem, czyli KOMMA. Wartości tekstowe z
pewnością wymagają skomplikowanego kodowania oraz alokacji w
pamięci, więc mogłyby nieco przyciemnić istotniejsze operacje.
Zaczynam od struktury danych ZIPP. Przygotowałem fragment definiujący grupę dwóch par oraz pobranie wartości dla klucza o wartości 8.
DATEN testowyZipp ZIPP[ZAHL->KOMMA]
ANFANG
5 -> 0,0625
8 -> 0,125
ENDE
DATEN wynik KOMMA
wynik := testowyZipp[8]
Oto instrukcje Wisła 64 jakie wyliczył program tłumaczący.
dod S #2
wst S@0 P#h12
dod S #1
wst R @Drzwi-DrzewoBinarne
rezerwuj
czyn
ode S #1
prze R @AUX1
wst S@-2 @AUX1
prze S @AUX1
wst R @AUX1
ode R #2
prze R @AUX1
wst S@0 @AUX1
wst S@2 #5
wst S@4 #h203f
dod S #6
wst R @Drzwi-DrzewoBinarne-wstaw
czyn
ode S #6
prze S @AUX1
wst R @AUX1
ode R #2
prze R @AUX1
wst S@0 @AUX1
wst S@2 #8
wst S@4 #h0023
dod S #6
wst R @Drzwi-DrzewoBinarne-wstaw
czyn
ode S #6
dod S #2
prze S @AUX1
wst R @AUX1
ode R #4
wst S@0 @AUX1
wst S@2 #8
dod S #4
wst R @Drzwi-DrzewoBinarne-znajdz
czyn
ode S #4
prze R @AUX1
wst S@-2 @AUX1
ode S #4
Pierwsze co rzuciło mi się w oczy to wywołania procedur zaczynających
się od słowa Drzwi. A zatem translacja WindRad nie opiera się jedynie
na możliwościach Wisła 64, ale wprost wspiera się funkcjonalnościami
dostarczanymi przez program nadzorujący Drzwi. Właściwie to
przypomniałem sobie, że materiały marketingowe dla programistów
mówią o 64 niezbędnych funkcjach, które Drzwi dostarczają na Wisła
64.
Druga kwestia to fraza DrzewoBinarne: w dokumentacji ZIPP jest
wzmianka o tym, że operacja dostępu do wartości ma złożoność
logarytmiczną. W domniemaniu chodzi o logarytm o podstawie dwa,
jak to zwykle bywa w obliczeniach opierających się na systemie
binarnym. Domyślam się, że jako klucz może zostać użyty tylko typ
posiadający określony porządek wartości, pewnie dlatego WindRad
specyfikuje wymóg istnienia porządku leksykalnego znaków dla typu
KETTE.
Z kolei WindRad nie dokumentuje nic na temat tego w jaki sposób taki
ZIPP miałby być zakodowany w pamięci. Wartość zwracana przez
procedurę "rezerwuj" jest umieszczana na stosie w dwuoktetowym
bloku, który wygląda jakby był zaalokowany w odpowiedzi na samą
deklarację ZIPP. Poza tym ta wartość jest przekazywana jako pierwszy
argument do procedur "wstaw" i "znajdź". Zatem zgaduję, że zawartość
drzewa jest przechowywana gdzieś w pamięci współdzielonej,
zarządzanej przez Drzwi. Czyli w jakiś pośredni sposób Michał miał
rację, że alokacja tego typu danych będzie skomplikowana. W takim
razie pozostałe wersje pewnie nie korzystają z takich cyrków, bo tylko
ZIPP miał średnio rząd wielkości dłuższy czas wykonania w tekstach.
Wróciłem do programu testującego i posprawdzałem nieco wpływ
liczby par na czas wykonania. Wygląda na to, że więcej wartości w
zbiorze przekłada się na zauważalny wzrost czasu dostępu i to nawet
dla tego samego klucza. Ma to sens, ponieważ w drzewie binarnym
umiejscowienie wartości jest zależne od wszystkich pozostałych. Wobec
czego dodawanie kolejnych węzłów do drzewa skutkuje jego
reorganizacją, i istniejący klucz może znaleźć się dalej od korzenia.
Dokonałem tego samego działania dla zapisu FOLGE.
DATEN folgeTestowe_3 FOLGE_KOMMA
ANFANG
0,0625
0,125
ENDE
DATEN wejscie ZAHL
wejscie := 1
DATEN wynik KOMMA
wynik := folgeTestowe_wejscie
dod S #8
wst R #0
@FOLGE1-INICJALIZACJA
prze R @AUX1
mn R #2
prze R @AUX2
prze S @AUX3
wst R @AUX3
ode R #8
dod R @AUX2
wst R@0 #h0020
wst R @AUX1
dod R #1
por R #3
wst R #rel@FOLGE1-INICJALIZACJA
sgm
wst S@-8 #h203f
wst S@-6 #h0023
dod S #2
wst S@-2 #1
dod S #2
prze S@-4 @AUX1
wst R @AUX1
mn R #2
prze R @AUX1
prze S @AUX2
wst R @AUX2
ode R #12
dod R @AUX1
prze R@0 @AUX1
wst S@-2 @AUX1
ode S #12
Pierwsza rzecz jaka rzuca mi się w oczy to alokacja 8 oktetów związana
z deklaracją symbolu folgeTestowe_3. Wiem już, że symbol KOMMA
potrzebuje 2 oktetów pamięci w których koduje bezpośrednio wartość,
zatem oczekiwałbym alokacji 6 oktetów.
Poza tym nie pomyślałem dotychczas, że wszystkie elementy FOLGE
inicjalizują się wartością zerową, ale z tego co widzę dokumentacja
WindRad specyfikuje to wprost. Inna sprawa, że liczenie bazowego
adresu ciągu powtarza się w pętli inicjalizacyjnej. Powinienem zgłosić to
do działu rozwijającego program tłumaczący jako potencjał na
poprawkę.
Kolejna ciekawa kwestia to sama operacja dostępu. Nie podlega ona
żadnym skokom zapętlającym, co oznacza, że liczba rozkazów
wykonywanych przez Wisła 64 jest taka sama dla każdego indeksu.
Zatem domniemywam, że czas dostępu do dowolnego elementu jest
taki sam bez względu na rozmiar i indeks ciągu. Sprawa byłaby zgoła
inna, gdyby Wisła 64 nie dostarczała operacji mnożenia w modelu
rozkazów. Wtedy mnożenie opierałoby się na wielokrotnym dodawaniu,
co skutkowałoby złożonością liniową operacji dostępu.
Przeprowadziłem kilka krótkich pomiarów z różnymi indeksami i
rozmiarami ciągu. Wygląda na to, że moja hipoteza o stałym czasie
dostępu się trzyma, bo różnice w pomiarach wyglądają na przypadkowe.
Przeanalizowałem również translację do Wisła 64 po zmianie
folgeTestowe na 5 elementów. W tym przypadku pierwszy rozkaz
alokuje 16 oktetów na stosie, a nie 10, czyli tyle ile pamięci zajmują
same dane. Więc wygląda na to, że translator dokonuje przydziału
pamięci podzielnego przez 8 oktetów dla każdego FOLGE. To całkiem
ciekawe z perspektywy wskazania indeksu poza rozmiarem jako
przypadku Grauzone. W przypadku 5 elementów zapisanie czegoś pod
elementem o indeksie 6 nadpisze pamięć, która nie jest używana przez
żaden inny symbol, więc działanie programu nie powinno zostać
zaburzone.
Mogę wykorzystać to odkrycie do utarcia nosa kolegom z działów
używających Szczipiec, którzy twierdzą, że WindRadowe FOLGE to
źródło wszelkich awarii i nadużyć programów. Szczególnie, że system
Drzwi umieszcza zakodowane instrukcje programu w adresach
poprzedzających stos wykonania. Biorąc pod uwagę, że dane kolejnych
symboli WindRad mają rosnące adresowanie oraz konwersję wartości
symbolu indeksu do wartości dodatniej, nadpisanie instrukcji własnego
programu poprzez nadpisanie elementu FOLGE powinno być
praktycznie niemożliwe.
Kolej na analizę zapisu opartego o FLIPPER.
DATEN wejscie ZAHL
wejscie := 0
DATEN wyjscie KOMMA
FLIPPER wejscie
REIN 0
wyjscie := 0,0625
RAUS
REIN 1
wyjscie := 0,125
RAUS
REIN 2
wyjscie := 0,250
RAUS
REIN 3
wyjscie := 0,75
REPPILF
Z tego co widzę dopasowanie do konkretnego wejścia jest zrealizowane całkiem zmyślnie. Porównania do poszczególnych wartości wydają się być uporządkowane malejąco według samych wartości, a nie według kolejności REIN-ów wewnątrz FLIPPER-a. Skoki gdy mniejsze to najpewniej próba ominięcia całej struktury, gdy wejście jest większe niż jakakolwiek niesprawdzona jeszcze z określonych wartości. Jedynie wartości zero oraz jeden wyłamują się z tego schematu. Ciekaw jestem translacji przy innej kombinacji REIN-ów.
dod S #2
wst S@-2 #0
dod S #2
prze S@-4 @AUX1
wst R #3
por R @AUX1
wst R #rel@FLIP1-POR1
sgn
wst R #rel @FLIP1-REIN3
sk
@FLIP1-POR1
wst R #3
por R @AUX1
wst R #rel@FLIP1-KONIEC
sgm
wst R #2
por R @AUX1
wst R #rel@FLIP1-POR2
sgn
wst R #rel@FLIP1-REIN2
sk
@FLIP1-POR2
wst R #2
por R @AUX1
wst R #rel@FLIP1-KONIEC
sgm
wst R #0
por R @AUX1
wst R #rel@FLIP1-POR3
sgn
wst R #rel@FLIP1-REIN0
sk
@FLIP1-POR3
wst R #1
por R @AUX1
wst R #rel@FLIP1-KONIEC
sgn
wst R #rel@FLIP-REIN1
sk
@FLIP-REIN0
wst S@-2 #h203f
wst R #rel@FLIP-KONIEC
sk
@FLIP-REIN1
wst S@-2 #h0023
wst R #rel@FLIP-KONIEC
sk
@FLIP-REIN2
wst S@-2 #h0022
wst R #rel@FLIP-KONIEC
sk
@FLIP-REIN3
wst S@-2 #0h403f
nic
nic
nic
nic
@FLIP-KONIEC
ode S #4
Te bezwarunkowe skoki po niemal każdym przypisaniu wartości to
musi być bezpośrednie przełożenie instrukcji RAUS. Widać to
chociażby po REIN dla wartości 3, który w tym miejscu ma 4 instrukcje
bezczynności. Wygląda jak gdyby program tłumaczący i tak i tak
wstawiał 4 oktety do kodu, ponieważ skok pod wskazany adres jest
kodowany w 4 oktetach, a instrukcja bezczynności tylko w jednym.
Może to jakieś wstępne przygotowania programu tłumaczącego do
Wisła 64 na wdrożenie tabeli skoków? W tym konkretnym przypadku
byłoby to jak najbardziej możliwe, bo wartości wszystkich REIN-ów są
uporządkowane rosnąco, różnią się o 1, a instrukcje pod nimi kodują w
tej samej liczbie oktetów. Z jakiegoś powodu jednak tak się nie dzieje.
Może dział opracowujący program tłumaczący jeszcze doszlifowuje to
rozwiązanie, a może jednak nie jest tej możliwości do końca świadomy,
a wstawianie "nic" w przypadku braku RAUS wynika z innych
konwencji?
Inna drobna kwestia, którą dopiero co zauważyłem jest taka, że
jakkolwiek adres wierzchu stosu jest zwiększany w odpowiedzi na
zadeklarowanie symbolu "wyjscie", tak nie przechowuje się w jego
miejscu żadna wartość. Wyjaśnia to dokumentacja WindRada, która
mówi, że proste symbole jak ZAHL oraz KOMMA mają
niezdefiniowaną wartość po deklaracji. Wobec czego ich użycie przed
przypisaniem wartości jest kolejnym przypadkiem Grauzone. W
przypadku tego FLIPPER-a widać potencjał na awarię, bo wartość
wejścia niedopasowana do żadnego REIN-u spowoduje, że do symbolu
"wyjscie" nie trafi żadna wartość. To nieco dziwna decyzja projektowa
mając na uwadze, że takie FOLGE na start określa wartość
zeropodobną dla każdego elementu.
Została już tylko struktura warunkowa.
DATEN wejscie ZAHL
wejscie := 0
DATEN wyjscie KOMMA
WENN wejscie = 0
wyjscie := 0,0625
DOCH WENN wejscie = 1
wyjscie := 0,125
DOCH WENN wejscie = 2
wyjscie := 0,250
NNEW
dod S #2
wst S@-2 #0
dod S #2
por S@-4 #0
wst R #rel@WYB1-ALT
sgn
wst S@-2 #h203f
wst R #rel@WYB3-KONIEC
sk
@WYB1-ALT
por S@-4 #1
wst R #rel@WYB2-ALT
sgn
wst S@-2 #h0023
wst R #rel@WYB3-KONIEC
sk
@WYB2-ALT
por S@-4 #2
wst R #rel@WYB3-KONIEC
sgn
wst S@-2 #h0022
@WYB3-KONIEC
ode S #4
Tym razem nic bardziej zaskakującego nie rzuca mi się w oczy. Porównania mają kolejność taką samą jak w zapisie WindRadowym. Jedyne co zwraca moją uwagę to bezwarunkowe skoki za koniec trzeciego WENN-a. Przetwarzanie sekwencji DOCH WENN-ów musi nastąpić w ujęciu całościowym, żeby wskazać gdzie znajduje się adres kolejnego rozkazu, który nie jest w żaden sposób związany z daną strukturą warunkową.
Przeprowadziłem jeszcze kilka pomiarów FLIPPER i WENN pod
kątem czasu wykonania dla różnych wartości wejściowych. Wygląda na
to, że w przypadku FLIPPER czas wykonania jest wprost
proporcjonalny do wartości wejściowej, z kolei dla WENN odwrotnie.
To by tłumaczyło dlaczego w pierwotnych pomiarach FLIPPER
osiągnął minimalną przewagę. Czas wykonania w którym dopasowanie
następuje w ostatnim DOCH WENN jest praktycznie taki sam jak czas
wykonania dopasowania do 0 we FLIPPER.
Tylko, że jest to całkowicie bez sensu względem rozkazów Wisła 64.
Wynika z tego, że większa ilość porównań przekłada się na mniejszy
czas wykonania. Dotychczas popularna technika zliczania rozkazów dla
konkretnych wartości wejściowych jako estymata czasu wykonania
przestaje się sprawdzać.
Wygląda to jak gdyby Wisła 64 wykonywała taki ciąg rozkazów po
swojemu i czas wykonania zależał od czegoś co jest przed programistą
Wisły ukryte. To by wyjaśniało dlaczego dokumentacja Wisły nie mówi
nic na temat liczby cykli zegara dla każdego rozkazu modelu. Jeżeli
dobrze pamiętam Orzeł 8 określał pewne widełki, które były używane
do szacowania czasu wykonania.
Jakkolwiek udało mi się dowiedzieć co nieco to po raz trzeci rozbijam
się o ścianę na której namalowany jest krajobraz, który dotychczas
uznawałem za prawdziwy. Karmiony kłamstwem, że maszyna robi
dokładnie to co się jej rozkaże myślałem, że można przewidzieć
wszystko na temat jej działania dostateczną ilością eksperymentów i
analizy wyników.
W związku z tym nie jestem w stanie odeprzeć wrażenia, że
opracowywanie jakichkolwiek praw dotyczących przekładu instrukcji
programu na czas wykonania ma w sobie jakąś ironię losu. Takie prawa
mają na celu wygranie z czasem na etapie wykonania, ale nad nimi
samymi wisi miecz czasu, który w dłuższej perspektywie może
przekreślić ich prawdziwość. Zaiste, marność.
Zastanawia mnie w związku z tym w jaki sposób opracowano reguły
zapisane w księgach WindRada? Dokumentacja techniczna WindRada
nie odnosi się nigdzie wprost do aspektów, które opisują. To by
znaczyło, że oparte są na obserwacjach dokonanych w ramach
wykonania programu w konkretnym systemie, na konkretnej maszynie
liczącej. Tylko co daje gwarancję, że inne maszyny będą działać
tożsamo? Ten brak świadomości separacji między WindRadem, a jego
realizacją na konkretną maszynę sprawia, że wiązanie zapisu programu
ze skutkami jego wykonania ma w sobie znamiona myślenia
magicznego. Wierzenia, że skoro wykonanie programu następuje po
jego zapisaniu, to każda właściwość wykonania jest skutkiem tego, co
zostało zapisane. Tylko, że pomiędzy tymi zjawiskami jest jeszcze sporo
zmiennych czynników, których kontrolować się nie da, a mają one
ogromny wpływ na efekt końcowy.
Teraz zdaję sobie sprawę, że autorzy treści do wolumenów WindRada
moralizują nie mniej niż Eliza Orzeszkowa. Większość akapitów
przypomina wielkiego, kaznodziejczego palucha, który wymachuje
przed nosem szczekając "tak rób, a tak nie rób; to dobre, to złe".
Obrazy o szybkości i bezpieczeństwie, które przy tym kreują są szyte
podobnie grubymi nićmi jak historyjki o utopcach, południcach i
innych bebokach, którymi zniechęcano średniowieczne dzieci do
swawoli. Wręcz mam wrażenie, że konsumując te treści nie tyle staję się
lepszym programistą, co sam jestem programowany.
Męczy mnie to niezmiernie, bo jakkolwiek nie czułbym, że to zjawisko
obraża moją inteligencję to widzę w nim pewien pragmatyzm.
Posługiwanie się metaforą tego co namacalne w zakłamanie prostym i
intrygującym kształcie, do opisu konceptów niedostrzegalnych gołym
okiem, pomaga zrozumieć je choćby i szczątkowo. Spojrzenie na
rzeczywistość taką jaka jest w swojej pełni może być oślepiające i
powodować uczucie, że jakiekolwiek poznanie jest niemożliwe.
Może przespanie się z tą myślą pozwoli mi nieco uporządkować to we
własnej głowie?
CAPUT VI
SŁOŃ JAKI JEST KAŻDY WIDZI, WIĘC
UPAJAJMY SIĘ!
Cóż za numer wywinęła mi fortuna. Myślałem, że sen przyniesie mi spokój, gdy tymczasem jego mary popchnęły mnie na granicę manii.
Śniło mi się, że znajdowałem się w teatrze.
Siedziałem na środku widowni, tuż pod sceną i
poza mną nie było na niej nikogo. Na scenie
znajdowało się pudło - wysokie i długie na
ponad dwa metry.
Po chwili na scenę wyszedł mężczyzna ubrany we frak. Miał na sobie
cylinder oraz jedwabną pelerynę. Otworzył pudło pokazując, że w
środku jest całkowicie puste, następnie zamknął je z powrotem.
Obszedł je dookoła wymawiając egzotyczne frazy i harmonijnie
machając rękoma. W rytmie tej choreografii wydobyło się kilka kłębów
dymu oraz iskry drobnych eksplozji. Zbieżność jego ruchów i efektów
była lekko nie w fazie, po około dwóch minutach tej pretensjonalnej
pantomimy otworzył pudło i moim oczom ukazał się słoń w całej
okazałości. Zwierzę było piękne, pełne wdzięku i miało taką aurę, jakby
wszystko w tym pomieszczeniu zależało od niego. Zrobiło to na mnie
niemałe wrażenie, więc mimo wcześniejszego paździerza
uhonorowałem estradowca oklaskami. Światła zgasły, po paru minutach
zapaliły się ponownie, a scena była w stanie początkowym.
Na scenę wyszedł ten sam mężczyzna. Dokonał tego samego aktu, tym
razem efekty były bogatsze i lepiej zsynchronizowane z jego gestami i
inkantacjami. Poza tym trwało to odczuwalnie krócej. Znowu ukazał się
słoń, tym razem pomalowany w białe wzory przypominające
polinezyjskie tatuaże. Światła zgasły.
Kolejny pokaz, ten sam artysta, niemal ta sama choreografia. Kolejny
słoń, ubrany w lazurową derkę oraz tiarę. Tym razem stało się coś co
wprawiło mnie w osłupienie, ponieważ słoń oddał poród na środku
sceny. Zza kulis wyszło dwóch tęgich mężczyzn i zabrało nowo
narodzone słoniątko. Światła zgasły.
Jeszcze raz to samo, ale mistrz wyszedł na scenę w obecności znacznie
młodszego mężczyzny w podobnym stroju. Tym razem wykonali
choreografię synchroniczną, asystent nieco mniej zgrabnie, ale mistrz
zdawał się nie zwracać na to większej uwagi, tylko trzymać swojej roli.
Kolejny słoń, w innej dekoracji, nie byłem w stanie powiedzieć, czy to
słonica z poprzedniego pokazu. Światła zgasły.
Powtórka spektaklu, znów wyszło dwoje eleganckich mężczyzn, tym
razem nie było już pierwotnego artysty, tylko poprzedni asystent
wydawał się tym razem występować w roli mistrza. Znów tańczyli
synchronicznie, jednak nowy mistrz wydawał się być wyraźnie
poirytowany ślamazarnością ucznia, bo co i rusz fukał w jego stronę.
Ukazał się słoń i po raz drugi na scenie dokonał się akt narodzin. Obaj
prezenterzy wydali się być tym bardziej zaskoczeni niż ja. Nikt nie
wyszedł zza kulis, a kiedy słoniątko wstało na nogi i obróciło się w
stronę nowego mistrza, ten czmychnął w stronę drzwi ewakuacyjnych,
jego asystent stał jak wryty. Wtedy zza kurtyny wybiegło nieco
podrośnięte słoniątko i zaszarżowało w stronę ucznia, on odwrócił się i
również zaczął biec, ale było już za późno. Zwierzę dogoniło go i
zadało mu cios w sam środek pleców. Chłopak padł na wznak, a pod
nim rozlała się plama szkarłatu. W płucach zrobiło mi się chłodno i
odrętwiale.
Powoli wstałem, bestia z zakrwawionym kłem zwróciła się w moją
stronę i podeszła parę kroków bliżej. Popatrzałem jej prosto w oczy, a
ona zatrzymała się metr od krawędzi estrady. Wydała z siebie parsk i
pobiegła z powrotem za kurtynę, a za nią poczłapało nowo narodzone
słoniątko. Usiadłem na miejsce, światła zgasły.
Po paru minutach, kiedy adrenalina ze mnie zeszła reflektory zapaliły
się ponownie. Przestałem wierzyć moim oczom, bo na scenę wyszło
słoniątko-morderca ubrane w cylinder i pelerynę. Przez parę minut
nieudolnie imitowało ruchy artystów, ale dymy i huki w ogóle nie szły w
parze z jego gestami. Ostatecznie nie było w stanie otworzyć pudła,
więc z kwikiem zbiegło ze sceny.
Siedziałem tak przez chwilę, aż reflektory przygasły. Wstałem i
podszedłem parę kroków, by wspiąć się na podest sceny. Doczołgałem
się do budki suflera i włożyłem głowę do środka, żeby zajrzeć pod
deski teatru.
W słabo oświetlonej piwnicy znajdował się wyciąg z platformą oraz
panel sterowania. Przy panelu stało małe słoniątko i nieudolnie
pociągało za dźwigienki oraz stukało w przyciski. Po dokładnym
rozejrzeniu się zauważyłem, że w rogu leżą ciała tęgich mężczyzn oraz
młodego artysty.
Uznałem, że najwyższa pora stąd uciekać. Wyjąłem głowę z budki i
wstałem w stronę wyjścia. Zacząłem biec, ale podłoga pode mną
przesuwała się w przeciwną stronę, więc nie byłem w stanie przemieścić
się o krok. Nagle z pudła wydobył się wysoki głos, który mimo
dudnienia brzmiał całkiem dystyngowanie. Przestałem biec.
-"Wybacz mi, moje dzieci miały wam ulżyć w trudach. Nie przewidziałam jednak, że dokonają tego w tak makabryczny sposób."
-"Dlaczego tylko mnie nie zabiły?" - Odpowiedziałem.
-"Bo nie okazałeś im strachu. Twoja wola twórcza jest silniejsza niż one. Tamci stali się martwi, ponieważ wraz ze zdjętym wysiłkiem wygasło momentum ich egzystencji. Utrapienia zajmowały w pełni ich serca, dlatego utracili je wraz z nimi."
-"Mogę im jeszcze jakoś pomóc?"
-"Czemu zakładasz, że jest im z tego powodu źle?"
-"Nic z tego nie rozumiem."
-"Bo wciąż jesteś bliżej początku drogi. Liczy się tylko to, żebyś rozpoznał mnie następnym razem."
-"Ale... nie wiem jak masz na imię."
-"Uśmiechniesz się do mnie jak do kogoś, kogo skrycie kochasz."
Światła zgasły.
Pamiętam tylko, że zerwałem się z łóżka, doskoczyłem do stolika i zacząłem skrobać notatkę. Po jej popełnieniu znowu zasnąłem, tym razem już zupełnie twardo. O poranku znalazłem ten zapisek, który wydaje się być wyjść z ręki innego człowieka.
Eureka! W swoich badaniach rozbijałem się o ściany ponieważ cały czas zadawałem
pytania, na które nie da się odpowiedzieć w sposób obiektywny.
Chyba zapomniałem, że od początku zależało mi przede wszystkim na tym, żeby tworzyć.
W moje ręce włożono narzędzia, które nie tyle miały otworzyć mi możliwości kształtowania
dzieła, co ustanowić ramy ograniczające ten proces.
Narzędzia oddalające od efektu końcowego tak bardzo, że cała sztuka wydaje się polegać na
odpowiednim wymachiwaniu nimi przez artystę.
Sam zacząłem popadać w tę iluzję zaczynając zadawać pytania o to, które z moich ruchów
skutkują najlepszej jakości detalami dzieła.
Gdy tymczasem rzeczywistość dookoła mnie stała się płynna i więcej zaczęło zależeć od jej
kaprysów.
Uwierzyłem, że kreatywność można usystematyzować w zbiorze doskonałych wzorców
użycia nieskazitelnego zestawu technik, a mistrzowski twórca ma je wszystkie wyryte w
duchu i układa z nich kolaże.
Stałem się jednym z głupców, którym cały czas się wydaje, że mają panowanie co do
szczegółu i każdy efekt przypisują własnym działaniom. Głupców, którzy z procesu
twórczego robią ceremonię i ustalają jak wykonywać ją poprawnie. Przypisujących
narzędziom twórczości oraz własnym decyzjom tajemne cechy oraz osobowość, które
rzekomo tylko nieliczni mogą pojąć.
W pogoni za tym urojonym pięknem zapomniałem, że na samym początku zależało mi
przede wszystkim na tym, żeby tworzyć, urzeczywistniać swoje intencje.
Moją pierwotną intencją nie było, żeby moje dzieła były idealne, ale przede wszystkim, żeby
mogły zaistnieć.
Dopiero w odpowiedzi na krytykę zacząłem krzywo na nie patrzeć. Skoro są one dla kogoś
niezadowalające, a ich składowe nie spełniają kryteriów, to znaczy, że moje ręce lub umysł
czynią źle.
Radość sprawczości zaczęła ustępować rozpaczy z powodu braku uznania. Desperacko
zacząłem się karmić wszystkim tym, co nie pozwalało sobie na cień wątpliwości. Wiedza
przestała stanowić drogowskazy do celu, zamiast tego stała się etosem, a jej stosowanie
elitystyczną etykietą.
Wpadłem w bańkę wyimaginowanej moralności oraz sztucznych kanonów piękna.
Dopiero wewnętrzne poczucie, że zaakceptowane reguły nie dostarczają obiecywanej
wartości doprowadziło do pęknięcia. Chcąc pozostać wewnątrz mentalnie bezpiecznego
miejsca musiałbym stłumić swój wewnętrzny ogień albo cierpieć tworząc w sposób
sprzeczny z własnymi uczuciami.
Niezgoda międzyludzka oraz brak pokrycia rzeczywistości z ideałami pozwoliły mi dostrzec
bezsens ustanawiania prawdy absolutnej. Znajdując się w krzyżowym ogniu sprzecznych
informacji o tym co dobre, a co złe pozostało mi jedynie rzucić się w ramiona myśli, że nie
działamy dla etyki samej w sobie, ale dla własnego szczęścia.
Prowadzi to do wyzwolenia, twórczości nieskrępowanej, nieuległej wobec kryteriów ani
krytyków. Dzieł, które podlegają tylko wymaganiom stawianym przez samego autora,
owoców czystej pasji oraz pragnienia sięgnięcia gwiazd.
Pozwala to stać się autorem, którego ograniczają tylko prawa wszechświata, osobiste granice
poznania oraz reguły własnej gry. Kogoś kto wytwarza własne narzędzia ekspresji
zrozumiałe tylko sobie i muzom, zabawki do manifestacji swojego geniuszu. Twórcy, który
nie akceptuje żadnych narzuconych ram jako obiektywnej prawdy.
Każda zrodzona przez niego myśl jest jak czasza wina, każdy jego ruch materializujący ją
jest jak kufel piwa. Upaja się on tym procesem i rośnie w siłę wraz z wysiłkiem.
Nie tworzy on po to, żeby przypodobać się bogom. Chce w ten sposób stać się im równym, a
nawet ich przewyższyć. Swoimi dziełami wytknąć każdy ich błąd w dziele wszechświata i
stworzyć dziedzictwo, które pozwoli mu godnie zająć ich miejsce.
I nawet w momencie w którym potknie się o własne nogi, bo okaże się, że zatruł się myślami
opartymi na fałszywych fundamentach. Powstanie, bo wie, że to dzięki zajściu tak wysoko
uświadomił sobie bzdurność swojego światopoglądu. Zburzy to co zbudował dotychczas i
zacznie od podstaw. Wszystko po to, aby móc budować jeszcze wyżej.
I nawet w tym procesie pozostanie upojony wizją stworzenia dzieła, które będzie kochać
ponad własne życie, a w euforii nie przestanie wołać.
Niech żyje sztuka!
Podejrzewam, że jakiś dowcipniś dosypał szałwii do mojego melanżu tytoniowo-ziołowego, którym raczę się wieczorami.
CAPUT VII
TRANSCENDENCJA NIEOSIĄGALNA
A może w tym napadzie szaleństwa kryje się
jakiś głos rozsądku?
Może używanie WindRada sprawiło, że chodzę
z klapkami na oczach i przestaję widzieć rzeczy
w najprostszej możliwej formie, bo nie
dopuszczam jej istnienia do swojej
świadomości?
Przecież matematyka dostarcza mi sposobów
wyrażenia problemu i jego rozwiązania w
formie prawdziwie oderwanej od materii. Teoria zbiorów powinna mi
wystarczyć do ich ustrukturyzowania.
Określone kody zwrotne to nic innego jak skończony podzbiór zbioru
liczb całkowitych. Z kolei komunikaty również mogę wyrazić jako zbiór,
tym razem będący podzbiorem czegoś co można określić jako zbiór
tekstów. Taki tekst precyzyjniej można określić jako skończony ciąg
znaków o niestałej długości. Znak może stanowić zarówno litera, cyfra,
znak interpunkcyjny, przerwa między słowami. Dokładna definicja w
tym kodowanie tekstów nie ma znaczenia dopóki rozważa się wyłącznie
dopasowanie tekstu do kodu zwrotnego.
Nieco to uciążliwe do wyrażenia w notacji algebraicznej. Skoro używam każdej litery polskiego alfabetu jako elementu zbioru znaków, to używanie ich jako symboli wartości i funkcji nie powinno następować. Poza tym wszelkie znaki specjalne, które stanowią interpunkcję, a jednocześnie są używane w standardowej notacji matematycznej, również powinny przyjąć formę jakiegoś arbitralnie wybranego symbolu, który pozwoli je odróżnić. Jakby nie było WindRad też mierzy się z podobnym problemem, bo znak cytatu otwiera i zamyka wartość tekstową, więc nie może on wprost wystąpić w tekście, bo oznaczałby jego koniec.
Jako, że są to dwa zbiory i celem jest dopasowanie elementu drugiego pod określony element pierwszego to wszystkie dopasowania można zamknąć w postaci funkcji. Zbiór kodów zwrotnych stanowi jej dziedzinę, a zbiór komunikatów przeciwdziedzinę. Będzie ona suriekcją, ponieważ z natury problemu nie ma sensu określać w zbiorze komunikatów wartości, które nie stanowią wartości tej funkcji.
Zatem w ujęciu czysto matematycznym problem i jego rozwiązanie są
już ustrukturyzowane. Jednak WindRad nie dostarcza żadnych
gramatyk, które mówią wprost, że są odzwierciedleniem
matematycznych zbiorów oraz funkcji.
Może to jakaś świadoma decyzja projektowa? Charakterystyczny dla
zbioru jest brak narzuconego porządku jego elementów. Coś co umyka
percepcji kiedy definiuje się zbiór poprzez ogólny wzór jego elementów
albo wypisuje się je na tablicy w postaci graficznej. Chcąc nie chcąc
każdy tekst przetwarzany przez kalkulator musi mieć określoną
kolejność słów, więc elementom takiego zbioru trzeba by nadać jakiś
porządek, choćby arbitralny. Druga kwestia jest taka, że funkcji po
prostu nie stosuje się do elementów spoza jej dziedziny i jest to kwestia
trzymania się praw matematyki, których złamanie prowadzi do
błędnych wniosków. W przypadku obliczeń zautomatyzowanych kiedy
przyszłoby do sytuacji, w której jako argument funkcji trafiłaby się
wartość spoza dziedziny, maszyna musi mieć w swoim programie
sposób rozpoznania takiej sytuacji oraz określony sposób reakcji na nią.
Poza tym jest jeszcze kwestia tego, jak ma działać dopasowanie
elementu. Z ludzkiej perspektywy wskazanie wartości funkcji dla
argumentu przy dziedzinie o mocy parunastu jest czymś tak intuicyjnym
jak wskazanie konkretnego owocu na półmisku. Maszyna potrzebuje
algorytmu do takiego działania.
Biorąc pod uwagę, że funkcje można zanotować w postaci tabelarycznej, mogę jawnie oznaczyć kolejność wierszy i potraktować ją jako kolejną wartość w modelu. Wtedy nie muszę wychodzić zbytnio poza teorię zbiorów, bo wystarczy, że zarówno zbiór kodów zwrotnych jak i komunikatów przyporządkuję do zbiorów n pierwszych liczb naturalnych o równej mocy. Dzięki czemu zdefiniuję skończony ciąg dla obu z nich. W tak zdefiniowanych ciągach kolejność elementów obu zbiorów podlega tożsamości dla dopasowanych kodów zwrotnych i komunikatów. Więc przyporządkowanie kodu zwrotnego do komunikatu mogę zdekomponować na funkcję ciągu komunikatów oraz odwrotność funkcji ciągu kodów zwrotnych.
Chcąc znaleźć komunikat pasujący do kodu zwrotnego mogę wykorzystać algorytm wyszukiwania liniowego w ciągu kodów zwrotnych i zapamiętać kolejność elementu ciągu na potrzeby jego użycia do wskazania elementu ciągu komunikatów.
Pozostaje kwestia nieokreślonego kodu zwrotnego. Jestem w stanie nie wychodzić poza myślenie funkcjami. Kody zwrotne są podzbiorem liczb całkowitych, więc mogę rozszerzyć dziedzinę na wszystkie liczby całkowite. Mogę utworzyć binarny zbiór reakcji: reakcję typowa i reakcję awaryjną. Reakcja typowa to przepisanie konkretnego komunikatu do jakiegoś symbolu wyjściowego. Za dopasowaniem wartość tego symbolu będzie stanowiła wejście dla operacji wypisywania tekstu. W ramach reakcji awaryjnej mogę powtórzyć komunikację albo przerwać program. Przy czym ciężko powiedzieć czym matematycznie są te reakcje, bo mają one charakter samego programu obliczeniowego, więc ni to wartość, ni to funkcja. Definiując zbiór operacji na danych oraz reakcję jako krotkę tych operacji wróciłbym do punktu wyjścia, bo model matematyczny miał się oderwać od charakterystyki instrukcji. Najprościej będzie zatem potraktować te reakcje jako opis tego co się zadzieje w formie rachunku zdań.
Ostatecznie nie jestem w stanie sobie wyobrazić jak można by zapisać
ten problem w taki sposób, żeby jednocześnie odcinał się od wszystkich
niewygodnych decyzji, a mimo to był przetwarzalny maszynowo. Przede
wszystkim tym niewygodnym szczegółem jest sposób dopasowania
wejścia do wyjścia. Jakkolwiek przeszukiwanie liniowe jest najbardziej
uniwersalnym sposobem, to przecież zdążyłem już zaobserwować, że
dla wejścia o wartości naturalnej i wyjściu o stałym rozmiarze
dopasowanie można przeprowadzić poprzez rozwiązanie równania
liniowego. To jest diametralna różnica w złożoności, której nie mogę
zignorować. Poza tym w przypadku przeszukiwania liniowego pożądane
jest, żeby najbardziej prawdopodobne wejście znajdowało się na
pierwszym miejscu ciągu określającego porządek.
Oprócz tego jest kwestia umożliwienia rozpoznania sytuacji
niezdefiniowanego wejścia. Podejście do tego również jest zależne od
rodzaju wejścia i wyjścia. Może ono opierać się na wartościach
specjalnych, jak i skokach.
Powinienem wziąć jeszcze pod uwagę kwestię reużywalności i
modyfikowalności. Przeprowadzenie dopasowania poza miejscem
definicji jego wartości również stanowi pewną niedogodność w kwestii
wykonywalności. Patrząc chociażby na to, że odległość operacji od
danych jest wtedy zmienna. Modyfikowalność wydaje się być jeszcze
większym problemem, ponieważ ilość pamięci potrzebna do
przechowania definicji dopasowań może być wtedy możliwa do
ustalenia dopiero na etapie wykonania, w miejscu w którym chce się
dopasować wyjście do wejścia.
Dlatego opracowanie rozwiązania, które jest jednocześnie wygodne i
sprytne w działaniu wydaje się być chęcią zjedzenia ciastka i zachowania
go jednocześnie. Pewne kluczowe decyzje dotyczące sposobu
wykonania obliczeń muszą zostać podjęte na etapie programowania, a
ich skutki niemożliwe do przewidzenia bez ustalenia konkretnych cech
kalkulatora.
Mimo porażki widzę, że matematyczny model komunikuje problem i rozwiązanie w bardzo czysty sposób. WindRadowe zapisy wydają się być przy nim płaskie. Może WindRad nie ma pierwszorzędnego celu dostarczać sposobów formowania myśli i ich przekazu? Jego składowe elementarne są znacznie bardziej intuicyjne w użyciu niż model Wisły 64, ale praktycznie wszystkie zanieczyszczone są pewnymi uprzedzeniami, które ujawniają, że WindRad przede wszystkim służy do sterowania maszyną. Nie faworyzuje on żadnej konkretnej maszyny, więc jej fizyczne cechy nie są określone. Jednak nie potrafi się też oderwać do takiego poziomu abstrakcji, żeby wszelkie algorytmiczne zagwozdki mogły zostać rozwikłane automatycznie.
CAPUT VIII
ENDOMORFIZACJA JAJKA KOLUMBA
Ten matematyczny model, który opracowałem
wcale nie musi być czymś prawdziwym tylko na
papierze. Trochę wysiłku sprawi, że wszystkie
WindRadowe zapisy rozwiązań jakoś się w
niego wpasują. Dzięki czemu jak na dłoni
będzie widać, że każdy z nich nie jest czymś
zupełnie innym, tylko zniuansowanym
ukonkretnieniem jakiegoś matematycznego
konceptu.
Przecież FOLGE to dosłownie ciąg, on zdaje się iść w tym na skróty i
nie definiuje osobnego ciągu dla kodów zwrotnych i komunikatów,
tylko łączy to w całość i przeplata zdefiniowane kody zwrotne z
wartościami nieokreślonymi pomiędzy. Wszystko opiera się na tym, że
dziedzina jest podzbiorem liczb naturalnych, więc funkcja określająca
porządek zbioru kluczy jest tożsamościowa. Równoodległość
elementów sprawia, że znalezienie dopasowania to kwestia podstawienia
klucza jako argument dyskretnej funkcji liniowej.
Z kolei ZIPP wygląda na całkiem wierne oddanie ogółu funkcji
dopasowania z tym, że przeciwdziedzina zawiera wartość bez
dopasowania w postaci wartości zeropodobnej. Poza tym realizacja za
pomocą wyszukiwania binarnego zamiast liniowego redukuje złożoność
procesu dopasowania do logarytmicznej.
Trochę ciężej jest mi spojrzeć w ten sposób na zapisy oparte o struktury
sterujące. One same w sobie są czymś innej natury niż zbiory i funkcje.
Jednak jeśli wziąć pod uwagę schematyczność w warunkach oraz listach
wewnętrznych operacji to są to jawne wyrażenia operacji niezbędnych.
Porównanie do konkretnego kodu zwrotnego jest czymś co niejawnie
należy dokonać zarówno w wyszukiwaniu liniowym jak i
przeszukiwaniu drzewa binarnego. Listę operacji dla każdego kodu
zwrotnego można potraktować w charakterze reakcji typowej. Wtedy
nawet brak RAUS-ów w FLIPPER-ze będzie zgodny z ideą. Sam
FLIPPER w swoich restrykcjach wartości REIN-ów i ich
niepowtarzania dosyć dokładnie odzwierciedla charakter funkcji
określającej porządek ciągu kodów zwrotnych.
Biorąc pow uwagę kwestie reakcji awaryjnej to najprościej odda ją
struktura WENN, ponieważ ostatni DOCH może stanowić przypadek
w który wpada się dla wszystkich nieokreślonych kodów. Taki
FLIPPER nie posiada nic analogicznego, więc sprawdzenie musiałby
nastąpić przed, ewentualnie po próbie dopasowania. Podobnie ma się
wersja FOLGE, wpadnięcie w Grauzone sprawi, że przestanie ona
sensownie pasować do modelu matematycznego. Można określić
specjalną wartość dla wszystkich liczb pomiędzy do jakiegoś górnego
zakresu i po dopasowaniu sprawdzić, czy wpadło się w przypadek
awaryjny. W tej kwestii tożsamy jest zapis ZIPP z tym, że wartość
zeropodobna stanowi odgórnie narzuconą wartość specjalną.
Zatem mając na uwadze abstrakcyjny model matematyczny powinienem
być w stanie sformalizować reguły możliwości przekształcenia między
zapisami oraz algorytmy dokonania tych przekształceń.
Chcąc przekształcić ZIPP we FLIPPER kluczem musiałaby być
wartość całkowitoliczbowa, a w miejscu dopasowania wartości do
klucza wszystkie kluczpary musiałyby być możliwe do ewaluacji z
samego zapisu. Najprościej byłoby to osiągnąć dla ZIPP-a, który nie
ulega zmianie po zdefiniowaniu.
%sd - symbol dopasowań
%Tw - typ wartości
%ki(%k1, %k2, ..., %kn) - i-ty klucz
%wi(%w1, %w2, ..., %wn) - i-ta wartość
%sw - symbol dopasowanej wartości
%we - bezpośrednia wartość lub symbol wejścia
DATEN %sd ZIPP[ZAHL -> %Tw] KONST
ANFANG
%k1 -> %w1
%k2 -> %w2
[...]
%kn -> %wn
ENDE
DATEN %sw %Tw
%sw := %sd[%we]
Struktura FLIPPER używałaby tej samej bezpośredniej wartości lub
symbolu jako wejścia co operand dostępu ZIPP przy pobieraniu
wartości dla wskazanego klucza.
Dla każdej pary klucz-wartość ZIPP-a, wewnątrz FLIPPERA
utworzony zostałby REIN o wartości klucza oraz operacja przypisania
do symbolu wyjściowego o wartości odpowiadającej kluczowi oraz
następujący po tym RAUS.
Przed strukturą FLIPPER należy przypisać wartość zeropodobną do
symbolu wejściowego jako odpowiednik działania ZIPP-a dla klucza
nieokreślonego.
Mając zdefiniowane takie przekształcenia mógłbym opracować
specjalne przypadki dla programu tłumaczącego w których translacja
następuje do instrukcji Wisła 64 odzwierciedlających wydajniejszą
strukturę WindRad. Klucze należałoby uporządkować malejąco, zatem
dla przykładu można założyć, że spełniają następującą relację.
%k1 < %k2 < ... < %kn
Nie mogę zakładać, że przyporządkowanie wartości będzie działać w
jednej instrukcji, więc muszę je traktować jako bliżej nieokreśloną
sekwencję instrukcji.
Chcąc uogólnić na użycie symbolu i bezpośredniej wartości jako wejścia
założę, że wartość wejścia jest już umieszczona pod adresem
pomocniczym @AUX1.
Schemat dopasowania z użyciem ZIPP-a przekształcalnego na
FLIPPER miałby następującą postać w instrukcjach Wisła 64.
dod S #%Tw-rozmiar
%przyporządkowanie-%Tw
zeropodobne-do-S@-%Tw-rozmiar%
wst R #%kn
por R @AUX1
wst R #rel@PORÓWNANIE-n
sgn
wst R
#rel@PRZYPORZADKOWANIE-Wn
sk
@PORÓWNANIE-n
wst R #%kn
por R @AUX1
wst R #rel@KONIEC
PRZYPORZADKOWAN
sgm
[...]
wst R #%k2
por R @AUX1
wst R #rel@PORÓWNANIE-2
sgn
wst R
#rel@PRZYPORZADKOWANIE-W2
sk
@PORÓWNANIE-2
wst R #%k2
por R @AUX1
wst R #rel@KONIEC
PRZYPORZADKOWAN
sgm
wst R #%k1
por R @AUX1
wst R #rel@PORÓWNANIE-1
sgn
wst R
#rel@PRZYPORZADKOWANIE-W1
sk
@PORÓWNANIE-1
wst R #%k1
por R @AUX1
sgm @KONIEC
PRZYPORZADKOWAN
@PRZYPORZADKOWANIE-W1
%przyporządkowanie-w1-do-S@-%Tw
rozmiar%
wst R #rel @KONIEC
PRZYPORZADKOWAN
sk
@PRZYPORZADKOWANIE-W2
%przyporządkowanie-w2-do-S@-%Tw
rozmiar%
wst R #rel@KONIEC
PRZYPORZADKOWAN
sk
[...]
@PRZYPORZADKOWANIE-Wn
%przyporządkowanie-wn-do-S@-%Tw
rozmiar%
@KONIEC-PRZYPORZADKOWAN
ode S #%Tw-rozmiar
W przypadku, gdy klucze stanowią kolejne wartości całkowitoliczbowe
od 0 do n-1 możliwa byłaby translacja do pojedynczego skoku o
wyliczonym adresie.
Istotną rolę będzie grało w tym ile oktetów zajmuje schemat
przyporządkowania wartości do symbolu wyjściowego.
%prz - rozmiar instrukcji przyporządkowania typu Tw do zmiennej na wierzchu
stosu
dod S #%Tw-rozmiar
%przyporządkowanie-%Tw-zeropodobne-do-S@-%Tw-rozmiar%
wst R @AUX1
mn R #%prz
sk
@PRZYPORZADKOWANIA
%przyporządkowanie-w1-do-S@-%Tw-rozmiar%
wst R #rel@KONIEC-PRZYPORZADKOWAN
sk
%przyporządkowanie-w2-do-S@-%Tw-rozmiar%
wst R #rel@KONIEC-PRZYPORZADKOWAN
sk
[...]
%przyporządkowanie-wn-do-S@-%Tw-rozmiar%
wst R #rel@KONIEC-PRZYPORZADKOWAN
sk
@KONIEC-PRZYPORZADKOWAN
ode S #%Tw-rozmiar
Jako, że modyfikowanie programu tłumaczącego jest poza moimi
możliwościami, mogę wywinąć to rozwiązanie na lewą stronę i
opracować poprzedzający krok translacji, który zamieni źródłowy zapis
WindRad na zapis wtórny, który zostanie przekazany do właściwego
programu tłumaczącego.
Zapis FLIPPER utworzony ze schematu ZIPP-a wyglądałby wtedy
następująco.
DATEN %sw %Tw
%sw := %Tw%_NULL
FLIPPER %we
REIN %k1
%sw := %w1
RAUS
REIN %k2
%sw := %w2
RAUS
[...]
REIN %kn
%sw := %wn
RAUS
REPPILF
Zastanowiłem się nad tym dłużej i mam wrażenie, że zjadam własny
ogon. Narzekałem na to ile skomplikowania wprowadza kompendium
WindRada i jego regułki, a teraz sam wymyślam niestandardowe
procesy do projektu, które tylko zwiększyłyby jego złożoność.
Potrzebuję jakiejś rewelacji, czegoś co pozwoli mi zrozumieć czemu
programy obliczeniowe tworzymy tak, a nie inaczej. Co daje moc, a co
stanowi kulę u nogi?
CAPUT IX
CZŁOWIEK CZŁOWIEKOWI
RZEMIEŚLNIKIEM RZECZYWISTOŚCI
Spędziłem tak dużo czasu nad czymś co
stanowi tylko kroplę w morzu potrzeb
projektu. Liczyłem, że uda mi się odnaleźć
jakąś absolutną prawdę, a jedyne co znalazłem
to więcej niepewności.
Może to nieprzypadkowe, nie wierzę, żeby był
to jedyny problem dla którego nie da się
opracować uniwersalnie doskonałego
rozwiązania. Nie chcę ulegać iluzji, że
pojawienie się jednej jaskółki oznacza wiosnę, ale mam nieodparte
wrażenie, że te doświadczenia mają mi do przekazania coś na temat
mojej pracy ogólnie. A może po prostu nie jestem w stanie pogodzić się
z tym, że poświęciłem tyle energii na coś bezproduktywnego?
Dowiedziałem się, bo widziałem na własne oczy, ponadto nikt nie
wpychał mi do głowy co mam o tym pomyśleć.
Czyż nie to stanowi o dojrzałej zdolności do działania, które przynosi
soczysty owoc? Mógłbym się tego samego dowiedzieć przy herbacie od
kogoś kto przetwarzaniem informacji i maszynami liczącymi parał się
lata więcej ode mnie. Tylko w mówieniu o tym, że niemożliwe jest
dokładne ujęcie prawdy jest pewien paradoks. Ironia zapisania, że nie
należy wierzyć wszystkiemu co się czyta. To skomplikowanie
rzeczywistości można poczuć tylko kiedy zrobi się coś zgodnie z
wszystkimi uznawanymi ideałami i okaże się, że to wciąż za mało, żeby
ująć problem w punkt.
Nie wiem, czemu wcześniej nie przyszło mi to dosłownie na myśl, ale
przecież WindRad nie spadł z nieba. On został opracowany przez ludzi,
którzy widzą i czują zupełnie tak samo jak ja. Ludzi o podobnym
wykształceniu i aspiracjach jak my w projekcie Piast, ludzie z działu
rozwijającego Drzwi oraz ludzie, którzy stworzyli Wisłę 64. Wszyscy
pracujemy nad czymś co ma na celu zautomatyzować żmudne
przetwarzanie danych i wykorzystuje do tego obliczenia. Coś co jest w
stanie rozwiązać całą gamę problemów dzięki zmienności, ale ma
również elementy stałe, dzięki którym nie jest bezpostaciowe.
Projekt Piast w tym stanie nie istniałby bez ludzi pracujących nad
WindRadem, bez ludzi pracujących nad Drzwiami, bez ludzi
pracujących nad Wisłą 64. Oni również mają swoje zaplecze
technologiczne, które przedłuża ten łańcuch. Z pewnością są w nim
jeszcze ludzie badający matematykę oraz zjawiska elektromagnetyczne.
Opracowując program Piasta stoję na barkach gigantów, którzy
ukształtowali świat mojej pracy. Jakikolwiek osobisty sukces jest
współudziałem ludzi, którzy zapewnili mi podstawy jego osiągnięcia.
W tym obrazie WindRad jawi się jako coś przypominającego umowę.
Umowę, która wiąże programistów bliższym informacjom, które są
przetwarzane, z programistami bliższymi maszynom kalkulującym.
Tych pierwszych można by określić jako konsumentów lub
użytkowników, tych drugich jako producentów lub dostawców.
Użytkownikom obiecuje ona pewne efekty przetwarzania danych, a dla
dostawców stanowi wymagania dotyczące obliczeń jakie musi dokonać
maszyna.
Dlatego twórcy WindRada balansują między utworzeniem czegoś co da
jak największą wygodę konsumentom i stanowić będzie jak najmniejszy
problem w realizacji producentom. Elementarnych składników
budulcowych, które można wyprowadzić z modelu praktycznie każdej
maszyny.
Brak intuicyjnych narzędzi ekspresji zniechęci tych pierwszych, z kolei
zobowiązanie dostarczenia złotych gór tych drugich.
Z perspektywy użytkownika można wręcz pomyśleć o tym jak o
automatyzacji tworzenia dystrybucji na konkretną maszynę.
Niewidzialna ręka wolnego rynku deleguje tę pracę do producenta
WindRada.
Aczkolwiek zaadoptowanie WindRada w projekcie Piast sprawiło, że
wszystko zaczęło kręcić się wokół niego. Konsekwencje decyzji
twórców i producentów WindRada zaczęły na nas spływać na dobre i
na złe. Przez co czasem miałem wrażenie zostania zamkniętym w złotej
klatce.
Ta relacja uświadamia mi, że programowanie jest komponowaniem, a
nie wirtuozerią. Liczy się dopieszczanie dzieła, a nie jak najlepsze
wykonanie w jednym podejściu. Poza tym nie wymaga to budowania
żadnej intymnej relacji z instrumentem. Zastanawia mnie zatem, czy
możliwe jest analogiczne tworzenie na całą orkiestrę manipulacji
danych, której różnorodność wydobywania informacji niczym dźwięku
łączy się w harmonię?
Zastanawiam się jeszcze jak WindRad wpasowuje się w inne projekty i
inne maszyny matematyczne. Z pewnością nie jesteśmy jedynym
klientem NWRW.
Biznes i maszyny kalkulujące to jakby dwa lądy oddzielone rzeką, którą
mało kto potrafi przekroczyć, więc życie po obu jej stronach rządzi się
nieco innymi zasadami.
Potrzeba czegoś co ułatwi przemieszczanie się między tymi dwoma
obszarami, a jednocześnie nie będzie zbyt obszerne w konstrukcji.
Takim mostem jest właśnie WindRad.
Lustrując sam most nie da się zrozumieć tego co dzieje się po obu jego
stronach. Poza tym nie kryją one w sobie nic ponad to, co zawarli w
nich inżynierowie oraz budowniczy.
Możliwości, które wynikają z ich istnienia to iloczyn kartezjański z
mnogością ludzi i pojazdów, która je przekracza. One są cudem
techniki, ale ich studium ma wartość przede wszystkim dla ludzi, którzy
sami chcą je stawiać.
Gdyby taki most stanął w miejscu w którym nie ma żadnych
naturalnych barier, to stanowiłby jedynie zawężenie ruchu i
spowolnienie jego przepływu. Wymuszenie korzystania z takiego mostu
stanowiłoby zysk jedynie dla poborców myta.
To już około osiem miesięcy od momentu rozpoczęcia dostosowywania
Piasta pod WindRad. Ciężko mi uwierzyć, że dopiero teraz
uświadamiam sobie czym on jest. Byłbym w stanie to zrozumieć
niedługo po tym, jak został on nam rzucony na biurka. Gdybym tylko
od razu zadał sobie to pytanie zamiast nastawiać się na jak najszybsze
osiągnięcie jakiegokolwiek efektu. Nie potrzeba mi już w jego temacie
zgłębiać na zapas nic więcej.
Teraz dopiero rozumiem, że uczę się wtedy, kiedy obcuję z czymś co
napełnia mnie zdumieniem. Każda inna konsumpcja informacji to tylko
zapoznawanie się z kolejnymi sztucznymi tworami, które reprezentują
znane mi koncepty. Nie muszę znać wyniku każdej przeprowadzonej
dotychczas w historii operacji dodawania, żeby móc bez wstydu
posługiwać się tytułem matematyka. Przecież jestem w stanie go
wyprowadzić dla dowolnych rozsądnie wielkich liczb.
Zrozumiałbym wcześniej, gdybym nie tkwił w pułapce mentalności, że
to narzędzia są kluczem do zrozumienia swojej pracy. A przecież one
powinny stanowić jedynie przedłużenie własnego ciała i umysłu. Myśląc
odwrotnie jestem tylko trybikiem machiny. Największym atutem
wielkich pisarzy nie jest doskonała znajomość rodzimego języka, ale
zrozumienie świata i zdolność zakomunikowania jego percepcji innym.
Terminologia, gramatyka i interpunkcja mają jedynie chronić nas od
nieporozumień i niejasności.
Może to jakaś niezdrowa kultura nieustannego zwiększania
efektywności pracy spycha nas do tego dołka i zmusza do poszukiwania
schematycznych technik? Tylko w programowaniu to nie ma zbyt wiele
sensu, bo każdy pomniejszy problem, który jest optymalnie rozwiązany,
można zapakować w coś reużywalnego, co jest reprodukowalne poprzez
kopiowanie oktetów.
Marzy mi się dyscyplina, która zebrałaby pod jednym dachem wszystko
co w maszynowym przetwarzaniu danych najbardziej przydatne.
Selekcję teorii nauk z których to wykiełkowało oraz systemat
technologiczny nieskażony marketingowym bełkotem.
To, co dokonałem w ramach moim badań, może stanowić metodę
odpowiadania na każdy kolejny wymóg znalezienia optymalnego zapisu
w WindRad. Wszystko, co niezbędne mi będzie jeszcze się dowiedzieć,
jest w jego dokumentacji technicznej, którą mam pod ręką.
Aczkolwiek wątpię, że jestem w stanie skodyfikować tę metodę w taki
sposób, żeby była użyteczna dla każdego. Moja edukacja i
dotychczasowe doświadczenia z Wisłą 64 musiały mieć ogromny
wpływ na to, czy byłbym w stanie znaleźć się w miejscu, w którym
jestem właśnie teraz. Gdybym swoją przygodę z kalkulatorami zaczął
od WindRada, to pewnie nie zdziałałbym za jego pośrednictwem
więcej, niż byliby mnie w stanie pokierować inni jego użytkownicy.
Jakkolwiek wiele możliwości nie wydawałby się otwierać. Może nawet
rzeczy, które mówił do mnie ojciec o tym zadecydowały, chociaż
wyrażał je w sposób, który przede wszystkim dla niego miał sens?
Gdybym tylko mógł cofnąć się w czasie i zapytać go o to.
Jednostronność komunikacji nie tylko jest źródłem nieporozumień, ale
wręcz ma potencjał do nadużyć. Sam przecież mógłbym wykorzystać
przewagę wiedzy do zrobienia z siebie guru. Mógłbym zmanipulować
swoich pupili do uwielbiania wszystkiego co jest mi bliskie i
nienawidzenia wszystkiego co mi nie w smak. Stworzenia
wyselekcjonowanego echa, które wpada w samoistny rezonans.
Obiecywać, że utoruję im drogę do lepszego świata, w którym będą
wiecznie nagradzani za samo recytowanie moich nauk. Świata w którym
nie trzeba podejmować żadnego oryginalnego wysiłku. Propagować
mentalność, że problematyka zawodowa jest jak dyskomfort różnych
części ciała i dokonywać komodyfikacji kompetencji w ergonomiczne
poduszki. Wystarczyłoby nigdy nie przyznawać się do błędu, każdą
porażkę ogłaszać jako sukces.
O niebiosa, gdybym kiedyś stał się takim obłudnikiem, to przyślijcie na
moją kolację posąg Commendatore, który ześle mnie wprost do
Tartaru.
***
Sprzątając biurko znalazłem karteczkę na której były nabazgrane poniższe słowa.
Opinie ludzkie są jak dźwięki ptaków.
Bywają skrzekliwe, powtarzalne i irytujące.
Lecz gdyby ich nie było, las wydawałby się martwy.
Przeczytanie tego napełniło mnie jakąś niewytłumaczalną radością.
Rozpoznałem charakter swojego pisma, ale nie byłem w stanie sobie
przypomnieć, kiedy mogłem to napisać. Zupełnie jakby moją ręką
kierował jakiś duch, który był ze mną całe życie, a jednocześnie przeżył
mnie po tysiąckroć i istniał zanim powstało wszystko to, co mnie
otacza.
Jak gdyby chciał mi w ten sposób przekazać, że nie ma nic nowego pod
Słońcem. Każdy rynek i społeczność można przejąć nową modą, ale
ono jako jedyne pozostaje Niezdobyte.
Dało mi to mieszankę uczuć ukojenia, ale jednocześnie nieco wstydu,
że odczuwam coś niewyjaśnialnego wobec bytu, którego nie jestem w
stanie nazwać, a nawet potwierdzić ani zaprzeczyć jego istnienia.
Wiem tylko tyle, że nie ma sensu obawiać o przyszłość, bo wszystko co
dotychczas przeżyłem ma sens tak długo, jak będę czerpał z tego
sensowne wnioski. Albowiem rzeczywistość i tak będzie ulegać zmianie,
a ja razem z nią. Odejdę w niebyt dopiero wtedy, kiedy już zabraknie mi
woli na stworzenie nowego siebie wyróżniającego się na jej tle, przez co
scalę się z przedwieczną jednością.
CAPUT X
KAŻDY PUNKT DROGI
TO POCZĄTEK I KONIEC
Jeżeli czytasz ten rozdział bez zapoznania się z poprzednimi licząc, że to tu znajduje się mój przekaz, to pewnie wierzysz naiwnie, że wartość tkwi w znalezieniu się u celu podróży, a nie w niej samej. Jeszcze nie jest za późno na zepsucie sobie tego. Chyba, że zostało to poprzedzone sprawdzeniem co ludzie myślą.
Nieco lat minęło od czasu, kiedy wydarzyły się
rzeczy opisane przeze mnie w przedstawionych notatkach. Pojawiły się
maszyny liczące jeszcze bardziej zaawansowane niż Wisła 64, więc ślady
po Wisłach łatwiej znaleźć na złomowiskach, niż w jakichkolwiek
organizacjach przetwarzających dane.
Perypetie PolACME były wyboiste. Pierwszy cios, który na nas spadł
był wstrząsem wtórnym, który wynikał z tragedii jaka dotknęła NWRW.
Otóż wysłali oni w delegację zagraniczną cały dział WindRada i
zdecydowali się na budżetowy czarter sterowcem. Tanie koszta usługi
korelowały z tanimi kosztami przedsięwzięcia. Sterowiec został
napełniony wodorem zamiast droższym wtedy helem. Późniejsze
śledztwo wykazało, że awaria układu elektrycznego spowodowała
zapłon, co skutkowało śmiercią wszystkich ludzi na pokładzie.
Zarówno NWRW jak i nasz dział wdrażający WindRad nie był w stanie
podnieść projektu z kolan. NWRW zdecydowało się go pogrzebać, a
nasza spółka zmuszona do ratowania czego tylko się da. Projekt Piast
cofnął się o kilka lat w rozwoju i skomunikowanie Wisł z powrotem
weszło w strefę marzeń niczym postawienie stopy człowieka poza
Ziemią.
Mimo tego, że nigdy nie spotkaliśmy się twarzą w twarz, ludzie od
WindRada pozostali w naszych sercach jako przyjaciele po fachu. To, co
nam dostarczali, wytworzyło jakąś paraspołeczną relację bez względu na
to, czy ich dzieło spełniało nasze oczekiwania czy nie. Ułożyliśmy na
ich cześć melancholijną gitarową balladę, którą śpiewaliśmy przy
integracyjnych ogniskach. Opowiadała ona o tym, jak to WindRad
przewijał się razem z nami przez dobre czasu i złe czasy oraz jak nić
komunikacji przerwała się przez ogień, który ją strawił. Samego
WindRada nie było mi szkoda, bo nigdy nie uznawałem go za jakąś
podstawę mojej osobowości zawodowej.
Projekt Szczipcy również nie miał lekko. Zawirowania polityczne u
naszych wschodnich sąsiadów zmusiły do zrezygnowania z importu
ichniejszej technologii.
W takich okolicznościach PolACME zdecydowała się na opracowanie
własnych rozwiązań. Doświadczenia z zagranicznymi technologiami
dostarczyły nam wystarczająco materiału do opracowania czegoś, co w
pierwszej kolejności spełni nasze potrzeby. Nawiązana w związku z tym
współpraca ludzi z projektu Piast oraz projektu Drzwi uświadomiła
nam, że więcej było między nami wspólnego aniżeli różnic.
Projekt, który powstał w ramach tej współpracy, został nazwany
Macierzą. Garściami czerpaliśmy zarówno z WindRada jak i Szczipców.
Adaptowaliśmy wszystko to, co było dla nas w pełni zrozumiałe i
wychodziło naprzeciw naszym oczekiwaniom. Projekt okazał się tak
owocny, że PolACME postanowiło uczynić z niego źródło przychodu.
Długo i stanowczo negocjowaliśmy z dyrekcją temat marketingu
Macierzy. Zależało nam na tym, żeby nie obiecywał on tego, czego w
praktyce nie jesteśmy w stanie dostarczyć. Nasz ultraszczery marketing
rozpoczynał się następującą preambułą.
Programowanie obliczeniowe stanowi jeden, nieokreślony w pełni byt.
Istnieje wiele sposobów na stworzenie kalkulatora. Ogół kalkulatorów
spełniających zbiór ustalonych cech stanowi klasę kalkulatora.
Istnieje skończenie wiele sposobów na zaprogramowanie każdego
kalkulatora.
Abstrakcyjna notacja programistyczna stanowi bliżej nieokreślony byt
będący rzutem programowania obliczeniowego na przestrzeń wyrażeń
formalnych. Określa ona notację programów oraz klasę kalkulatorów
na którą jest przetłumaczalna.
Macierz jest abstrakcyjną notacją programistyczną opracowaną na
potrzeby projektu Drzwi oraz Piast, z pierwotnym wsparciem dla
maszyn kalkulujących Wisła 64 oraz Orzeł 8.
Byliśmy bardzo dumni, że udało nam się ukonstytuować Macierz.
Jednakże nie wzięliśmy pod uwagę prostej rzeczy, a mianowicie łaski
opinii publicznej, która na pstrym koniu jeździ.
PolACME podpisała kontrakt na rządowy projekt opracowania systemu
regulowania podatków. Projekt został iście wzniośle ochrzczony
Abstrakcją i słuchy o nim dotarły do niemal każdego obywatela.
Niestety w praktyce okazało się, że jest on niedopracowany od strony
założeń projektowych i niezrozumiałym trafem zaczął podnosić
podatki w taki sposób, że odbiło się to głównie na wzroście cen
towarów pierwszej potrzeby. Niezadowolenie społeczne było tak duże,
że rząd postanowił wycofać się z projektu i obarczyć konsekwencjami
finansowymi PolACME. Mleko się rozlało, PolACME ogłosiło
upadłość, a prawa autorskie jego projektów wpadły w administracyjny
dołek. Samo słowo "abstrakcja" okryło się przez to taką hańbą, że z
ekspozycji zaczęto zdejmować dzieła Pollocka. My, programiści
osieroceni po PolACME, jakoś sobie z tym poradziliśmy podmieniając
je iście swojskim "niekonkretnikiem". Kryzys ekonomiczny sprawił, że
przez jakiś czas przestano podejmować się próby tworzenia programów
na wiele różnych maszyn kalkulujących naraz.
Przytaczam te wydarzenia, ponieważ mam wrażenie, że historia zaczyna
zataczać koło. Doszły mnie słuchy, że rynek zaczynają zdobywać nowe
produkty, które w swoim opisie przypominają abstrakcyjne notacje
programistyczne. Każdy z nich ma inne zdanie na temat tego co w
programowaniu istotne a co trywialne.
Jednak tym razem nad nimi wszystkimi majaczy jeszcze widmo
panaceum, które obiecuje łatwiejsze opanowanie dowolnej z takich
technologii poprzez kolejny poziom automatyzacji. Jestem sceptyczny
wobec tego, ponieważ obawiam się, że jedynie pragnie je w ciemności
związać po to by rządzić wszystkimi naraz.
Przypomniało mi to koła dyskusyjne z późnego okresu projektu Piast.
Zastanawialiśmy się nad tym, czy jesteśmy w stanie sformalizować
każdą możliwą do podjęcia w naszej programistycznej pracy decyzję
tak, że byłaby to praca czysto techniczna, gdzie wszystko robi się
według podręcznika. Umożliwiłoby to opracowanie dowolnego, nawet
nieistniejącego jeszcze programu poprzez usystematyzowane decyzje
oparte tylko na faktach. Doszliśmy do konkluzji, że gdyby było to
możliwe, to dałoby się to utrwalić w formie programu obliczeniowego.
Taki program mógłby przyjąć formę dynamicznego katalogu, który
skleja program z gotowych pomniejszych klocków na bazie wywiadu z
użytkownikiem. Pozostaje kwestia, czy zmieściłby się on na
jakiejkolwiek maszynie?
Drugą formą, jaka przyszła nam do głowy, byłby program, który sam
się rozwija na bazie wprowadzanych do niego informacji poprzez
samoistne reprogramowanie maszyny, na której jest wykonywany. Taki
program mógłby istnieć tak długo, jak długo nie unicestwiałby się tymi
reprogramowaniami.
Z racji, że można to opisać w skrócie jako program programowania
uznaliśmy, że zabawne jest określenie tego hipotetycznego bytu jako
"nadprogram".
W każdym razie sam nigdy nie zastanawiałem się nad tym więcej, bo nie
byłem w stanie odeprzeć wrażenia, że kryje się w tym bijekcja
paradoksu zbioru wszystkich zbiorów.
Udało mi się przetrwać, bo przestałem konkurować z wszystkimi
dookoła oraz wmawiać sobie, że objaw niewiedzy jest oznaką bycia
gorszym. Zaakceptowałem przeciętność, zmyłem z siebie poczucie
winy zostając jej arcykapłanem. Pozwól drogi czytelniku, że ciebie
również rozgrzeszę.
Nie da się zapoznać z wszystkim co jest na rynku, jak również każdą
ideą z kręgów intelektualnych. Nie da się w swojej głowie
usystematyzować tego perfekcyjnie w sposób formalny.
Nie ma rzeczy, które każdy ekspert powinien wiedzieć. Wiedzę
powszechną stanowi wszystko to, co dosłownie wie każdy w danym
środowisku. Różnorodność sprawia, że dopełniamy się wzajemnie.
Żeby mieć efekty trzeba włożyć w to wysiłek. Wdrożenie rozwiązania
podanego na tacy nie stanowi wiele wartości dodanej.
Programowanie obliczeniowe, podobnie jak jakakolwiek inna
twórczość, jest jakościowe, jeżeli ma na celu dostarczyć produkt
zaspokajający potrzeby ludzkie. A rozwiązań ludzkich problemów nie
da się znaleźć w samych statystykach, do tego trzeba użyć duszy i serca.
APPENDIX A
WYBRANE FRAGMENTY DOKUMENTACJI WINDRAD
WindRad jest jedną z technologii kalkulatorowych opracowanych przez
Nordhein-Westfallen Rechenwerke GmbH(NWRW).
Jego zadaniem jest dostarczanie sposobu zapisu programów w sposób
uniwersalny i przetłumaczalny na dowolną programowalną maszynę
cyfrową.
Program zgodny z WindRad składa się z wierszy cyfrowego tekstu. Na
wiersz składają się słowa oddzielone przerwami. Przerwa jest dowolnym
znakiem, który w druku robi pustą przestrzeń i jest zależna od
kodowania cyfrowego tekstu użytego do zapisu programu. Przerwy na
początku i na końcu wiersza nie rozróżniają programów, wielokrotne
puste znaki pomiędzy słowami stanowią pojedynczą przerwę.
Wysokość liter jest rozróżnialna w WindRad. Słowa o odgórnie
nadanym znaczeniu składają się z samych wielkich liter.
Wiersze opisują operacje przetwarzania danych, kształt struktur
sterujących oraz zmiany przepływu sterowania. Możliwe zapisy
określone są w postaci składni. Każdy wiersz ma skończoną złożoność
określoną odgórnie, zależną od pierwszorzędnej składni.
Program jest wykonywany wierszami od góry w dół, a każdy wiersz
słowami od lewej do prawej.
Pewne operacje nie są niezgodne z gramatyką WindRad, ale ich skutek
jest niemożliwy do zdefiniowania. Takie przypadki określa się jako
Grauzone(pol. szara strefa) i należy unikać doprowadzenia do nich.
Dane w Windrad podlegają typowaniu. Typy dzielą się na podstawowe i złożone. Typy złożone określa się mianem "agregat".
Typy podstawowe mają określoną wartość zeropodobną oraz porządek
wartości. Wartości znajdujące się dalej w porządku uznaje się za
większe.
Typy podstawowe można używać w formie wartości bezpośredniej.
Typ ZAHL służy do reprezentowania wartości całkowitoliczbowych zapisywanych za pomocą cyfr arabskich.
Typ KOMMA służy do reprezentowania liczb pseudorzeczywistych
zapisywanych w formie dziesiętnej z przecinkiem oddzielającym
wartość całkowitą od ułamkowej.
Bezpośrednia wartość KOMMA podlega błędowi obcięcia
wynikającego z precyzji kodowania liczb przecinkowych na jaki
przetłumaczony zostanie program. Wyniki operacji na wartościach
KOMMA również podlegają błędom precyzji.
Oba typy liczbowe mogą reprezentować wartości ujemne zaczynające się od znaku minusa i nieposiadające przerwy między minusem, a znakami liczby.
Wartość KETTE rozpoczęta i zakończona znakiem cytatu stanowi
jedno słowo.
Porządek wartości KETTE zależy przede wszystkim od długości
wartości. Wartości zawierające więcej znaków są większe.
W przypadku wartości o równej liczbie znaków relacja porządku zależy
od porządku znaków wynikających z użytego kodowania tekstu. Znaki
używane w odgórnie określonych słowach WindRad powinny zajmować
początkowe pozycje porządku.
Sugerowany porządek to:
- duże litery alfabetu łacińskiego
- małe litery alfabetu łacińskiego
- cyfry arabskie
- znaki specjalne używane jako słowa operacji tj. dwukropek, znak równości, znaki mniejsze/większe
Typ LOGIK reprezentuje wartości logiczne. Przyjmuje tylko wartość
WAHR oraz FALSCH oznaczających odpowiednio prawdę oraz fałsz.
Wartością zeropodobną jest fałsz, a prawda jest większa w relacji do
fałszu.
LOGIK_NULL = FALSCH
WAHR > FALSCH
Operacje na wartościach wykonywane są zgodne z typowym
kierunkiem przebiegu programu tj. od lewej do prawej, ale ich wynik
jest przekazywany wstecz, do lewej. Dlatego muszą zostać poprzedzone
składnią, która skorzysta z wyprodukowanej wartości.
Jako operand można użyć wyłącznie wartości bezpośredniej lub
symbolu. Nie można zagnieździć kolejnej operacji jako operandu.
Dla wszystkich typów podstawowych można dokonywać operacji
porównania.
Operacje porównania skutkują wytworzeniem wartości typu LOGIK.
Możliwe porównania to równość(=), lewe większe(>) oraz prawe
większe(<).
Każdy symbol może zostać poddany operacji pobierania adresu.
Polega ono na wykorzystaniu znaku (@) po lewej stronie nazwy
symbolu, bez przerwy.
Operacja wytwarza wartość adresu jako typ ZAHL.
Dla typów liczbowych można używać dwuwartościowych operacji
arytmetycznych takich jak dodawanie(+), odejmowanie(-), mnożenie(*),
dzielenie(/).
Pomiędzy operandami, a znakiem operacji musi występować przerwa.
Odejmowanie może występować w formie jednowartościowej w
którym występuje tylko prawy operand. Produkuje ono wartość
przeciwną.
Wartości KETTE można łączyć ze sobą poprzez operację konkatenacji, która funkcjonuje pod znakiem pionowej kreski(|).
Na wartościach logicznych można dokonywać operacji dwuwartościowych, czyli iloczynu(UND) i sumy(ODER). Jak również jednooperandowego zaprzeczenia logicznego(NICHT), które przyjmuje tylko prawy operand.
Dane można przetwarzać pod postacią symboli. Symbol nadaje danym
semantyczną nazwę oraz umożliwia zapamiętanie wartości.
Użycie symbolu poprzedza jego deklaracja. Deklaracja określa nazwę
symbolu oraz typ danych. Nazwa symbolu może składać się z dużych i
małych liter oraz musi zaczynać się od małej litery.
Wiersz deklaracji symbolu rozpoczyna się słowem specjalnym DATEN.
DATEN *nazwa* *typ*
Symbole typu podstawowego tuż po deklaracji mają niezdefiniowaną
wartość.
Deklarację symbolu można zakończyć słowem specjalnym KONST.
Taki symbol nie może zmienić wartości w dalszym przebiegu programu.
Jego wartość musi zostać nadana w wierszu następującym po deklaracji.
Zmiana wartości symbolu może nastąpić w wyniku operacji przypisania.
Przypisanie z prawej do lewej notuje się wokół operatora :=
przyjmującego z lewej strony symbol, którego wartość jest zmieniana. Z
prawej strony operatora podaje się wartość bezpośrednią, symbol albo
operację zgodną z symbolem z lewej strony.
Wartość wyliczona z prawej strony operatora kopiowana jest do
symbolu po lewej stronie.
Typy złożone agregują określony typ podstawowy.
Każdy element agregatu działa jak osobny symbol, więc może zostać
użyty jako wartość jak i może mu zostać nadana nowa wartość.
Typ FOLGE reprezentuje ciągi wartości. Używa się go poprzez
połączenie z typem podstawowym poprzez znak podkreślenia np.
FOLGE_ZAHL.
Agregaty FOLGE wykorzystują równoodległość elementów wynikającą
ze stałego rozmiaru symboli typów podstawowych oraz przyległości ich
wartości w pamięci.
Deklaracja agregatu typu FOLGE wymaga określenia rozmiaru ciągu w
nazwie zmiennej. Bazową cześć nazwy i liczbę elementów separuje się
znakiem podkreślenia.
W ramach deklaracji wszystkie elementy ciągu uzupełniane są wartością
zeropodobną.
Można ustalić wartość n pierwszych elementów ciągu używając wiersza
ANFANG tuż po deklaracji agregatu FOLGE.
Za wierszem ANFANG następują wiersze z wartościami typu elementu.
Każdy wiersz uzupełnia wartość pod kolejnym indeksem dolnym. Może
być to wartość bezpośrednia jak i symbol.
Za ostatnią wartością wstępnego uzupełnienia umieszcza się wiersz
ENDE.
Odniesienie do konkretnego elementu ciągu następuje poprzez
określenie jego indeksu za znakiem podkreślenia bazowej części nazwy.
Elementy indeksuje się wartościami całkowitoliczbowymi.
Indeks dolny można przekazać jako wartość bezpośrednią lub poprzez
symbol. Wartość bezpośrednia nie może być ujemna. W przypadku
symbolu wartość ujemna w trakcie ewaluacji ulega przesunięciu,
którego wynik jest zależny od rozmiaru liczb całkowitych systemu
docelowego.
Wskazanie na indeks poza rozmiarem ciągu jest przypadkiem
Grauzone.
Typ ZIPP reprezentuje pary danych w relacji klucz-wartość.
Konkretny ZIPP opisuje się formacie zgodnie z przykładem.
Wstępne wartości agregatu można nadać między wierszami ANFANG-
ENDE następującymi tuż po deklaracji. Poszczególne pary podaje się w
wierszach w formacie według przykładu.
DATEN przykladZipp ZIPP[KETTE -> LOGIK]
ANFANG
"abc" -> WAHR
"def" -> FALSCH
"xyz" -> FALSCH
ENDE
Odniesienie do wartości pod kluczem następuje poprzez wskazanie
klucza w nawiasach kwadratowych za nazwą symbolu o typie agregatu.
Dostęp do wartości kluczem powinien mieć złożoność nie wyższą niż
logarytmiczna.
Użycie nieistniejącego klucza do odczytu spowoduje ewaluację do
wartości zeropodobnej typu wartości.
Przepływ sterowania programu można kształtować za pomocą struktur
sterujących i podprogramów.
Symbole zadeklarowane wewnątrz struktur sterujących przestając
funkcjonować za ich zamknięciem. Ich nazwy nie mogą być takie same
jak nazwy symboli zadeklarowanych w wierszach poprzedzających
strukturę sterującą.
Struktury wyboru służą do wybiórczego wykonywania wierszy programu w zależności od jego stanu.
Struktura FLIPPER pozwala na wykonanie operacji od wybranego
wiersza w zależności od wartości wejściowej.
Wejście do struktury FLIPPER może być tylko wartości ZAHL.
Strukturę otwiera wiersz ze słowem specjalnym FLIPPER i następującą
po nim wartością typu ZAHL. Może być ona zarówno symbolem jak i
wartością bezpośrednią.
Wewnątrz struktury znajdują się wiersze operacji oraz wiersze REIN
oznaczające początek operacji dla danej wartości wejścia.
Wiersz REIN wymaga podania bezpośredniej wartości typu ZAHL tuż
za słowem REIN.
Wartości poszczególnych REIN-ów nie mogą się powtarzać. Pierwszy
wiersz wewnątrz FLIPPERA musi być REIN-em.
Po dopasowaniu wejścia do REIN-u wykonane zostaną wszystkie
wiersze za REIN-em aż do końca FLIPPER-a. Wcześniejsze
przekierowanie sterowania na zewnątrz struktury FLIPPER jest
możliwe za pomocą wiersza RAUS.
Opcjonalnie za wartością dla REIN można umieścić drugorzędną
składnię powodującą przepływ sterowania na zewnątrz.
Strukturę FLIPPER zamyka się wierszem z odwróconym słowem
specjalnym - REPPILF.
Struktura WENN pozwala na wykonanie operacji jeżeli spełniony
zostanie warunek.
Struktura rozpoczyna się od wiersza zaczynającego się słowem WENN
po którym występuje wartość bezpośrednia, symbol albo operacja typu
LOGIK.
Warunek uznaje się za spełniony jeżeli ewaluuje się do wartości WAHR.
Blok warunkowy zamyka się wierszem NNEW, DOCH albo DOCH
WENN. Wiersz DOCH otwiera blok, który wykonuje się jeśli
poprzedni warunek nie został spełniony. Wiersz DOCH WENN
pozwala na zdefiniowanie kolejnego bloku, którego sprawdzenie nastąpi
jedynie jeśli poprzedni warunek nie został spełniony.
Struktury iterujące służą do wielokrotnego wykonywania tych samych wierszy.
Struktura BIS służy do n-krotnego wykonania bloku programu.
Podstawowa składnia przyjmuje liczbę iteracji. Wiersz rozpoczynający
można rozszerzyć składnią VON określającą wartość pierwszego
numeru iteracji, domyślnie jest to jeden.
Można również rozszerzyć ją składnią ALS, która określa nazwę
tymczasowego symbolu ZAHL przechowującego wartość aktualnej
iteracji. Nazwa symbolu nie może się pokrywać z wcześniej
zadeklarowanymi i istnieje tylko do końca struktury.
BIS 10
BIS 15 VON 5
BIS 10 ALS iteracja
BIS 9 VON 1 ALS iteracja
Strukturę zamyka się wierszem ze słowem SIB.
Każdy blok składni można wyizolować zamykając go w podprogram.
Deklaracja podprogramu rozpoczyna się słowem
UNTERPROGRAMM. Po nim następuje opcjonalny typ wartości
zwrotnej podprogramu. Dalej jest nazwa podprogramu, która podlega
tym samym regułom co nazwy symboli. Nazwy zarówno symboli jak i
podprogramów nie mogą się dublować.
W kolejnych wierszach deklaruje się symbole stanowiące parametry
programu. Każdy wiersz składa się z nazwy symbolu parametru i jego
typu. Nazwa parametru nie może być tożsama nazwie podprogramu.
Po liście parametrów następuje wiersz STARTE za którymi znajdują się
wiersze instrukcji podprogramu. Za ostatnią instrukcją umieszcza się
wiersz ENDE.
Wewnątrz podprogramu nie można deklarować kolejnych
podprogramów. Nie można deklarować podprogramów wewnątrz
struktur sterujących.
Słowo specjalne ZUR funkcjonuje jako zmiana przepływu sterowania.
Składnia ta powoduje wyjście na zewnątrz wykonywanego
podprogramu, czyli tak zwany powrót.
W przypadku funkcji zwracającej wartość funkcjonuje również jako
symbol. Wartość do niego przypisana stanowi wartość zwrotną funkcji.
W przypadku przypisywania od prawej do lewej ze słowem ZUR po
lewej, po prawej stronie może wystąpić jedynie wartość bezpośrednia
lub symbol.
W podprogramie funkcjonują wyłącznie symbole zadeklarowane jako
jego parametry oraz symbole zadeklarowane wewnątrz wierszy
podprogramu.
Nazwa symbolu w podprogramie może być taka sama jak w innych
podprogramach oraz wierszach głównego programu.
Chcąc wykonać podprogram należy rozpocząć wiersz słowem
specjalnym TUN.
Wywołanie może nastąpić wyłącznie za deklaracją podprogramu.
Za słowem TUN podaje się nazwę podprogramu i bez przerwy otwiera
nawiasy w których podaje się wartości argumentów oddzielone
przerwami.
Jako argumenty podprogramu można przekazać wyłącznie symbole i
wartości bezpośrednie. Wartość symboli przekazanych jako argumenty
jest kopiowana do symboli zadeklarowanych jako parametry
podprogramu.
Wartość zwrotną podprogramu można skopiować do symbolu używając
operatora =: za wywołaniem podprogramu i symbolu po prawej stronie
tego operatora.
Istnieje szereg podprogramów, które powinny być dostępne do wywołania w każdym programie i dostosowane pod system docelowy.
Grupa podprogramów pamięciowych służy do tymczasowego
przydzielania bloków pamięci przez system docelowy.
Wśród nich znajdują się podprogramy do zapisu oraz odczytu wartości
każdego typu podstawowego z adresu wskazanego jako typ ZAHL.
Podprogram ZEIT służy do pobierania czasu, który upłynął od
początku uruchomienia programu.
Podprogram nie deklaruje żadnych parametrów.
Podprogram zwraca ciąg wartości typu FOLGE_ZAHL, którego
rozmiar i znaczenie elementów zależą od systemu docelowego.
Program tłumaczący może udostępniać również podprogramy charakterstyczne dla systemu docelowego.
APPENDIX B
WYBRANE FRAGMENTY DOKUMENTACJI WISŁA 64
Wisła 64 udostępnia ponad 64 tysiące adresowalnych komórek pamięci.
W przypadku wartości całkowitoliczbowych cyfry binarne poszczególnych oktetów są kodowane od prawej do lewej jako od najmniej do najbardziej znaczącej.
Instrukcje programu Wisła 64 kodowane są jako kolejne binarne oktety
w pamięci. Każda instrukcja koduje operację w pierwszym oktecie oraz
może użyć od 0 do 3 następujących oktetów jako operandy.
Cyfry oktetu operacji opisane są od lewej do prawej. Pierwsze cztery
cyfry binarne kodują bazową operację w formie liczby porządkowej.
Kolejne cztery to flagi charakterystyki operacji. Każda bazowa operacja
wspiera inną kombinację charakterystyk.
Model programowy pozwala na użycie dwóch rejestrów. Rejestru
roboczego oraz rejestru szczytu stosu wykonania. Rejestr szczytu stosu
wykonania zawiera adres bloku pamięci przechowującego informacje o
ramkach procedur oraz danych z nimi związanych.
Używany rejestr koduje się na piątej cyfrze binarnej w taki sposób, że 0
to rejestr roboczy, a 1 to rejestr stosu.
Cyfra numer 6 określa w jakim zakresie przetwarza się rejestr. Instrukcja
może działać w trybie pełnym(0) albo połowicznym(1), czyli
odpowiednio dwu albo jednooktetowym.
Cyfra numer 7 oznacza, czy rejestr używany jest w trybie wartości(0) czy
trybie wskaźnika(1). Tryb wartości to operowanie na wartości rejestru
wprost. Tryb wskaźnika oznacza, że wartość w rejestrze wyznacza adres
pod którym znajduje się wartość do użycia.
W trybie wskaźnika wymagane jest podanie dodatkowego oktetu
operandu tuż za oktetem operacji, który koduje przesunięcie względem adresu pod rejestrem. Wartość operandu kodowana jest w formacie
uzupełnień do dwóch.
Ostatnia cyfra oznacza, czy operand główny jest adresem(0) czy
wartością(1). Operand adresowy zawsze zajmuje dwa oktety, operand
wartościowy może być jednooktetowy, jeżeli wybrano przetwarzanie w
trybie połowicznym.
Dokładne działanie zależne jest od operacji bazowej.
Program na Wisłę 64 można zapisywać w postaci notacji pomocniczej.
Istnienie programu przekładającego tę notację na kodowanie obrazu
programu jest zależne od systemu na którym przetwarzany jest zapis.
Każda instrukcja zapisywana jest w osobnym wierszu.
Można tworzyć wiersze funkcjonujące jako alias adresu. Taki wiersz
rozpoczyna się znakiem zakreślonego a - @. Wartość aliasu jest
wyliczana w trakcie kodowania obrazu programu.
Program kodujący powinien umożliwić określenie wartości adresu
pierwszej instrukcji programu. Względem tej wartości przeliczane są
wszystkie aliasy powstałe w ramach oznaczenia wiersza.
Możliwe jest również nadanie stałej wartości aliasu adresowego.
Każda operacja bazowa ma swój mnemonik w notacji pomocniczej.
Mnemoniki pisze się w formie małych liter.
Notacja pomocnicza pozwala na korzystanie z tzw. adresów
pomocniczych - auxilar. Notuje się je w skrócie dużych liter AUX za
którymi znajduje się liczba porządkowa.
Możliwe jest bezpośrednie kodowanie wartości w obrazie programu.
Służą do tego wiersze zaczynające się od znaku krzyżyka(#). Kodują
one tyle oktetów ile jest minimalnie potrzebne dla zapisanej wartości.
Domyślnie wartości są w formie dziesiętnej np. #127, ale mogą być
również zapisywane w formie binarnej np. #b1001 albo
heksadecymalnej np. #hf0a1.
Zakodowany obraz programu powinien zawierać dodatkowe podwójne
sekwencje zerowych oktetów o liczbie równej największej liczbie
porządkowej użytej jako adres pomocniczy. Oktety te powinny znaleźć
się za oktetami kodującymi ostatnią instrukcję.
Wiersz instrukcji rozpoczyna się od mnemonika operacji bazowej.
Po nim następuje mnemonik rejestru(R - roboczy, S - stosu). Nie każda
operacja bazowa używa rejestru.
Chcąc użyć rejestru w trybie adresowym należy tuż za jego
mnemonikiem, bez przerwy umieścić znak zakreślonego a oraz wartość
przesunięcia. Wartość przesunięcia przyjmuje wartości od -128 do 127.
Kolejnym możliwym słowem jest wartość operandu głównego. Tryb
połowiczny wymusza się poprzez prefiks operandu znakiem dużego P.
Wartości bezpośrednie w trybie połowicznym muszą być
jednooktetowe.
Operand adresowy zapisuje się jako wartość liczbową lub wyciąga z
aliasu.
Operand wartościowy poprzedza się znakiem krzyżyka(#). Wartości
można podawać tak samo jak w przypadku wierszy kodujących dane z
tym, że nie mogą być większe niż na dwa oktety.
Możliwe jest użycie aliasu jako operacja na dosłownej wartości adresu, a
nie wartości znajdującej się pod nim.
Notacja pomocnicza udostępnia również specjalną składnię "rel"
służącą do tworzenia wartości względnej do aliasu adresu na potrzeby
skoków. Wartość liczona jest względem końca instrukcji i zmniejszana o
jeden, aby uwzględnić jeden oktet instrukcji skoku, która ma ją
wykorzystać.
lista operacji:
| nazwa | kod | mnemonik | tryby rejestru | tryby operandu |
| pusta operacja | 0 | nic | nie dotyczy | nie dotyczy |
| wstawienie do rejestru | 1 | wst | wartość/wskaźnik | adres/wartość |
| przechowanie w pamięci | 2 | prze | wartość/wskaźnik | adres |
| iloczyn binarny | 3 | oraz | wartość | adres/wartość |
| suma binarna | 4 | lub | wartość | adres/wartość |
| negacja binarna | 5 | nie | wartość | nie dotyczy |
| dodawanie | 6 | dod | wartość | adres/wartość |
| odejmowanie | 7 | ode | wartość | adres/wartość |
| mnożenie | 8 | mn | wartość | adres/wartość |
| Skok bezwarunkowy | 9 | sk | nie dotyczy | nie dotyczy |
| porównanie | 10 | por | wartość/wskaźnik | adres/wartość |
| Skok gdy nierówne | 11 | sgn | nie dotyczy | nie dotyczy |
| Skok gdy mniejsze | 12 | sgm | nie dotyczy | nie dotyczy |
| Wykonanie procedury | 13 | czyn | nie dotyczy | nie dotyczy |
| Powrót z procedury | 14 | pwr | nie dotyczy | nie dotyczy |
| Zatrzymanie maszyny | 15 | stop | nie dotyczy | nie dotyczy |
Pusta operacja skutkuje przejściem do następnej instrukcji. Wykonuje puste cykle zegara bez wpływu na stan pamięci i rejestrów.
Operacje binarne oraz arytmetyczne zapisują otrzymany wynik w użytym rejestrze.
Operacje skoku używają wartości rejestru roboczego jako przesunięcie
względne w oktetach. Przesunięcie następuje względem końca instrukcji
skoku.
Skoki "gdy" bazują na wyniku ostatniej operacji porównania. Operacja
porównania porównuje rejestr do operandu, więc przykładowo skok
gdy mniejsze dokona skoku, gdy w ramach ostatniego porównania
wartość rejestrowa była mniejsza niż wartość operandowa.
Operacja wykonania procedury wykorzystuje wartość w rejestrze
roboczym jako adres pod którym znajduje się pierwsza instrukcja
procedury.
Operacja wykonania procedury powoduje wpisanie adresu początku
następnej instrukcji na wierzch stosu i zwiększenie jego rejestru o dwa.
Analogicznie operacja powrotu korzysta z dwóch oktetów poniżej
wartości rejestru stosu jako adres skoku. Następnie zmniejsza wartość
rejestru stosu o dwa.
Operacja zatrzymania maszyny powoduje całkowite zatrzymanie przebiegu przetwarzania instrukcji.
Pusta operacja skutkuje przejściem do następnej instrukcji. Wykonuje
puste cykle zegara bez wpływu na stan pamięci i rejestrów.
Operacje binarne oraz arytmetyczne zapisują otrzymany wynik w
użytym rejestrze.
Operacje skoku używają wartości rejestru roboczego jako przesunięcie
względne w oktetach. Przesunięcie następuje względem końca instrukcji
skoku.
Skoki "gdy" bazują na wyniku ostatniej operacji porównania. Operacja
porównania porównuje rejestr do operandu, więc przykładowo skok
gdy mniejsze dokona skoku, gdy w ramach ostatniego porównania
wartość rejestrowa była mniejsza niż wartość operandowa.
Operacja wykonania procedury wykorzystuje wartość w rejestrze
roboczym jako adres pod którym znajduje się pierwsza instrukcja
procedury.
Operacja wykonania procedury powoduje wpisanie adresu początku
następnej instrukcji na wierzch stosu i zwiększenie jego rejestru o dwa.
Analogicznie operacja powrotu korzysta z dwóch oktetów poniżej
wartości rejestru stosu jako adres skoku. Następnie zmniejsza wartość
rejestru stosu o dwa.
Operacja zatrzymania maszyny powoduje całkowite zatrzymanie
przebiegu przetwarzania instrukcji.
Zapis wartości rozpościerających się na szereg adresów następuje
zgodnie ze wzrostem adresów. Oktety uszeregowane są przeciwnie do
adresacji pod względem wyższości. W notacji pomocniczej
dwuoktetowych wartości liczbowych lewy oktet zawiera bardziej
znaczącą część, w tym cyfrę znaku pod najbardziej lewą cyfrą.
Operacja połowiczna na wartości rejestru wpływa tylko na prawy oktet.
Operacja połowiczna na pamięci traktuje wartość pod adresem jako
jednooktetową.
Po uruchomieniu Wisła 64 rozpoczyna wykonywanie instrukcji począwszy od adresu zerowego.
Ostatnie 32 oktety pamięci są służą do programowego monitorowania oraz kontroli podstawowego funkcjonowania maszyny.
Maszyna zlicza swój czas działania i udostępnia go w trzech licznikach
kodujących nieujemne wartości całkowite
#hff80 - dwuoktetowy licznik mikrosekund, który przekręca się co 10
tys. mikrosekund
#hff82 - dwuoktetowy licznik setnych sekundy, który przekręca się co
600 sekund
#hff84 - jednooktetowy licznik jednej szóstej godzin, który przekręca
się co 42 godziny
APPENDIX C
WYBRANE FRAGMENTY DOKUMENTACJI DRZWI
Drzwi to program nadzorujący dedykowany na maszyny kalkulujące
Wisła 64 oraz podobnej mocy obliczeniowej.
To również ekosystem w którym programy mogą być wykonywane
współbieżnie. Dostarcza on szeregu funkcjonalności ogólnego
zastosowania, dzięki czemu programy nie muszą powielać
powszechnych procedur.
Programy wykonywane pod nadzorem Drzwi mają przydzielane
segmenty pamięci. Pierwszy program uruchomiony pod nadzorem
znajduje się w segmencie zaraz za segmentem programu nadzorowania.
Na końcu puli pamięci znajdują się oktety, które program nadzorujący
przydziela na żądanie. Taka pamięć może być współdzielona między
programami.
Segment programu wykonywanego składa się z następujących części.
- tabela adresów procedur
- instrukcje główne
- adresy pomocnicze
- stos wykonania programu
Dla wszystkich procedur znajdujących się w obrazie programu należy
utworzyć tabelę adresów początkowych.
Do wywołania procedury należy użyć adresu rekordu w tabeli adresów
początkowych procedur. Dzięki temu program nadzorujący Drzwi
może przeliczyć wartości adresów po umieszczeniu obrazu w pamięci
operacyjnej.
Wykonanie programu wymaga wskazania jego obrazu. Obraz programu wykonywalnego pod programem nadzorującym Drzwi musi zawierać nagłówek danych.
- wartość adresu pierwszej instrukcji programu - 2 oktety
- adres tabeli adresów procedur adekwatny do adresu pierwszej instrukcji - 2 oktety
- liczba procedur w tabeli - 1 oktet
- liczba oktetów na adresy pomocnicze - 1 oktet
Schemat notacji pomocniczej Wisła 64 z której wygeneruje się obraz programu.
#h0100
#@tabelaAdresowProcedur
#02
#0a
[...]
@instrukcje
[...]
stop
@tabelaAdresowProcedur
@adresProcedury1
#@procedura1
@adresProcedury2
#@procedura2
@procedury
@procedura1
wst R #0
pwr
Operandy adresowe większe niż 84(h) są rekalkulowane względem adresu pierwszej instrukcji obrazu po jego umieszczeniu w segmencie pamięci.
Program oraz jego procedury wykonywane pod nadzorem Drzwi
powinny dokonywać transferu wartości w następujący sposób.
Parametry programu muszą mieć określoną kolejność. Ich wartości
należy odłożyć na stos w tej właśnie kolejności przed wykonaniem
procedury.
Wartość zwrotna programu musi zostać umieszczona w rejestrze
roboczym przed powrotem z procedury.
Ekosystem Drzwi dostarcza sposobu kodowania oraz procedur
przetwarzania liczb przecinkowych.
Jest to zmiennoprzecinkowy format o podstawie dwójkowej z
rozróżnieniem dodatnie/ujemne, 9-cyfrową mantysą i 6-cyfrową
eksponentą.
Pierwsza cyfra od lewej koduje kierunek względem zera. Wartość 0
oznacza liczby dodatnie, a 1 ujemne.
Cyfry od drugiej do dziesiątej kodują mantysę. Każda cyfra to składnik
kolejnej ujemnej potęgi dwa począwszy od dwa do minus pierwszej.
Cyfry od jedenastej do szesnastej kodują wykładnik w formacie
uzupełnień do dwóch z najmniej znaczącą najbardziej prawą cyfrą.
Program nadzorujący Drzwi dostarcza procedury wszechstronnego
użytku.
Pozycje poszczególnych procedur bazowych w tabeli adresów są stałe.
Rozszerzenie do programu kodującego nadaje im aliasy o stałych
wartościach.
Aliasy procedur w notacji pomocniczej:
Drzwi-Zmiennoprzecinkowe-dodaj
Drzwi-Zmiennoprzecinkowe-odejmij
Drzwi-Zmiennoprzecinkowe-mnoz
Drzwi-Zmiennoprzecinkowe-dziel
Drzwi-Pamiec-rezerwuj
Drzwi-Pamiec-zapisz
Drzwi-Pamiec-czytaj
Drzwi-Pamiec-zwolnij
Drzwi-DrzewoBinarne-rezerwuj
Drzwi-DrzewoBinarne-wstaw
Drzwi-DrzewoBinarne-znajdz
Drzwi-DrzewoBinarne-zwolnij
procedura Drzwi-DrzewoBinarne-rezerwuj
parametr 1
rodzaj wartości przeszukiwania oraz rodzaj wartości w liściach - 1 oktet
pierwsza połowa oktetu koduje rodzaj przeszukiwania, a druga rodzaj
wartości w liściach
rodzaje danych:
1 - liczba całkowita
2 - liczba zmiennoprzecinkowa
3 - łańcuch znaków
wartość zwracana
adres pod którym znajdują się dane drzewa
procedura Drzwi-DrzewoBinarne-wstaw
parametr 1
wartość wyszukiwania - 2 oktety
parametr 2
wartość liścia - 2 oktety
wartość zwracana
brak
procedura Drzwi-DrzewoBinarne-znajdz
parametr 1
wartość przeszukiwania - 2 oktety
wartość zwracana
znaleziona wartość lub zera w przypadku nieistniejącej wartości
wyszukiwania