Nejspíš by bylo korektní, aby tento text začal obvyklým sypáním si popela na hlavu. Vyjádřen by tím byl nejen opakující se pocit lítosti nad odklonem od ústředního tématu - RPN, ale také sebekritické přiznání neschopnosti zapudit potřebu převádět arabská čísla na římská a zpět použitím kalkulátoru HP-50g. Ne, nic takového, žádná sebereflexe! Kdo ví, jestli by to někdo nepokládal za slabost... Pojďme se raději věnovat všemi oblíbenému konverznímu tématu.
Námět | ↑ ↓ |
Při pohledu na jednotlivá předchozí řešení je nápadný kvalitativní rozdíl zejména mezi variantou UserRPL a provedením v assembleru pro procesor Saturn. Aby ne! Bez nadsázky - střetávají se zde dva zcela odlišné světy. Nemá smyslu zabývat se srovnáváním pracností vytváření programu assembler vs. UserRPL. Zajímavější bude věnovat se otázce, zda by program v UserRPL nemohl obsahovat některé finesy použité ve výrazně výkonnější verzi psané v jazyku symbolických adres. Odpověď lze intuitivně vytušit: jasně, že by mohl.
Snadnost přístupu k jednotlivým cifrám zadání v případě převodu A→R je v assembleru tak nestoudně okatá, že nějaké dělení s odřezáváním desetinné části mu za žádných okolností nemůže konkurovat. Rozhodně důvod k důkladnému přepracování.
Převod opačným směrem je také vhodné zrevidovat. Pro ilustraci bude užitečné oživit si obsah modulu v jazyku C roman.c. Na první pohled je vidět, že funkce r2a() obsahuje ve formě lokální proměnné tabulku svalt[] s hodnotami odpovídajícími jednotlivým římským cifrám. Také je zde volána funkce r2a_prevlessnum() pro zjištění počtu předchozích číslic s menší hodnotou. Rozhodně ne na první pohled, ale po důkladném zkoumání modulu roman.a, je vidět, že zde není ekvivalent ani tabulky ani pomocné funkce (absence vysvětlena v modulu r2a2.c, ale o tom až později). Je to zjednodušení, které rozhodně stojí za zvážení.
Řešení | ↑ ↓ |
Úprava vedoucí ke snazšímu přístupu k cifrám zadání je velmi jednoduchá. Číslo je převedeno na textový řetězec sekvencí R→I →STR. Ten je dále "obráběn" příkazy HEAD a TAIL. Výsledný kód se tím zkrátí, zjednoduší a zpřehlední - no, učiněná nádhera. Ale co to? Doba provádění programu po těchto úpravách povážlivě vzrůstá! Takže zpátky na stromy - dělení s odřezáváním desetinné části je dost dobré. K tomu všemu přistupuje logaritmus a exponenciální funkce (viz LOG a ALOG), ale pořád to běží rychleji, než původní verze. Mein Gott, das ist unglaublich!
Je to trochu divné, ale pro kalkulátor je evidentně snazší jakýkoliv výpočet s operandy typu 0, tedy Real number. Ani operand typu 28, což je Real integer, není žádnou výhrou. I jeho použitím se běh programu citelně zpomalí. A ostatní typy (List, String, ...) znamenají ještě větší spotřebu výpočetního výkonu. Daleko větší úlevu přineslo odstranění lokálních proměnných → divisor sidx result a také provádění všech operací pouze s položkami RPL stacku.
Převod R→A je složitější, řešení bez použití lokálních proměnných z praktických důvodů nepřipadá v úvahu. Hlavní změnou je přiblížení se k postupu, který je obsažen v modulu r2a2.c. Podle jeho vzoru se nové řešení obejde bez dvou lokálních proměnných: tabulku hodnot římských cifer a výsledek rozdělený podle desítkových řádů do seznamu, tedy → svalt result. Také následující lokální proměnná → cval s aktuální položkou z tabulky hodnot chybí. To sice vypadá skvěle, ale jak říká úvodní věta, bez lokálních proměnných to nejde. Jejich jména → ←pidx addt subt →ord (poslední v pořadí odpovídá makru THIS_ORD) včetně označení hodnot RPL stacku v komentářích zdrojového textu odpovídají symbolům použitým v r2a2.c.
Počet lokálních proměnných je sice vyšší než u původní verze, ale i tak to stálo za to. Dvě nová pole addt[] resp. subt[] spolu s hodnotou less umožní dokonalejší kontrolu správnosti zadání. Ale co hlavně: i tenhle druh převodu běží rychleji, než původní verze. Wunderschön!
Pozn.: Provedená repase vyřešila i jednu zanedbanou chybu v původní verzi: vstupní hodnota typu Real number je v ní vyhodnocena jako chybné zadání. Scheiße, das ist schrecklich!
Pro změření rychlosti postačí krátká sekvence 'ROMAN' 3888 OVER TEVAL UNROT SWAP TEVAL NIP. V tabulce jsou naměřené výsledky:
Převod | Doba [s] pro řešení | Úspora [%] | |
---|---|---|---|
původní | nové | ||
A→R | 0.3492 | 0.2863 | 18 |
R→A | 1.6614 | 0.9603 | 42 |
Výsledek je to moc pěkný i přesto, že nový kód je o 106 bajtů delší. Pro HP-50g to je zcela zanedbatelné množství paměti a ani poměrný nárůst zvíci necelých deseti procent není nijak hrozný.
Zdrojový text | ↑ ↓ |
« IF { 0. 28. } OVER TYPE POS @ pos value THEN @ je to cislo DUP I→R IP DUP @ ipval ipval value 1. < SWAP 3999. > OR @ 0./.1 value SWAP 0. ROT @ 0./.1 type value ELSE DUP TYPE @ type value CASE DUP 2. == @ je to retezec THEN END DUP 6. == @ je to algebraicky vyraz THEN @ bude preveden na retezec SWAP →STR DUP SIZE 1. - 2. SWAP SUB SWAP END DROP @ value →STR "Bad Argument: " SWAP + DOERR END OVER SIZE DUP @ size size type value 1. < SWAP 15. > OR @ 0./.1 type value END IF THEN DROP @ value "Bad Range: " SWAP →STR + DOERR END "IVXLCDM" @ sromt type value → sromt « IF @ type value THEN @ value 0. @ less value sromt SIZE @ pidx less value {0. 0. 0. 0.} DUP @ subt addt pidx less value « ←pidx 1. - 2. / IP 1. + » → ←pidx addt subt →ord « DO @ less value OVER HEAD sromt SWAP @ digit sromt less value POS @ aidx less value IF DUP NOT THEN @ aidx less value DROP2 "Unknown Digit: " SWAP + DOERR END SWAP OVER ←pidx @ pidx aidx less aidx value IF < THEN @ if(aidx < pid) DROP 0. @ less = 0; ELSE @ else 1. + @ ++less; END CASE @ less aidx value DUP 3. > THEN DROP2 @ value END OVER ←pidx ≤ THEN END @ less aidx value DUP 1. ≤ THEN 'addt' @ 'addt' less aidx value →ord EVAL @ _ORD() 'addt' less aidx value DUP2 @ _ORD() 'addt' _ORD() 'addt' less aidx value GET @ addt[] _ORD() 'addt' less aidx value OVER @ _ORD() addt[] _ORD() 'addt' less aidx value 'subt' SWAP ROT @ addt[] _ORD() 'subt' _ORD() 'addt' less aidx value PUT @ _ORD() 'addt' less aidx value 0. @ 0. _ORD() 'addt' less aidx value PUT @ less aidx value END DROP2 @ value END IF DUP TYPE @ typ neni Real ale String THEN "Unexpected Digit: " SWAP + DOERR END SWAP '←pidx' @ 'pidx' aidx less value STO @ pidx = aidx; 'addt' →ord EVAL @ _ORD() 'addt' less value DUP2 @ _ORD() 'addt' _ORD() 'addt' less value GET @ addt[] _ORD() 'addt' less value ←pidx 1. - 2. / FP 8. * 1. + @ _INC() addt[] _ORD() 'addt' less value + @ addt[] _ORD() 'addt' less value DUP 4. ROLLD @ addt[] _ORD() 'addt' addt[] less value PUT @ addt[] less value IF 9. > THEN DROP "Too much Digits: " SWAP + DOERR END UNTIL SWAP TAIL SWAP @ less value OVER SIZE @ size less value NOT END DROP2 addt subt @ subt addt » - @ { } 1. @ 1. { } « NSUB 1. - ALOG * » @ « » 1. { } DOSUBS @ { } ∑LIST R→I @ result ELSE I→R IP @ value "" @ result value DO SWAP DUP LOG IP @ order: dekadicky rad aktualni cifry DUP @ order order value result ALOG @ divisor: delitel pro ziskani nejvyssi cifry ROT @ value divisor order result DUP PICK3 @ divisor value value divisor order result / IP @ digit: aktualni cifra ROT @ divisor digit value order result OVER * @ aktualni cifra na puvodnim radu NEG @ -d000 digit value order result ROT @ value -d000 digit order result + @ value digit order result 4. ROLLD SWAP @ order digit result value 2. * 1. + @ sidx: index do sromt { 4. 9. } @ {4 9} sidx digit result value PICK3 @ digit {4 9} sidx digit result value IF POS @ 0./1. sidx digit result value THEN @ je to 4 nebo 9 sromt @ sromt sidx digit result value OVER DUP @ sidx sidx sromt sidx digit result value SUB @ sromt[] sidx digit result value UNROT 1. + @ sidx digit sromt[] result value SWAP @ digit sidx sromt[] result value 9. == @ 0./1. sidx sromt[] result value + @ sidx sromt[] result value UNROT @ sromt[] result sidx value + @ result sidx value sromt @ sromt result sidx value ROT DUP @ sidx sidx sromt result value SUB @ sromt[] result value + @ result value ELSE @ neco jineho nez 4 nebo 9 UNROT @ digit result sidx value WHILE DUP REPEAT PICK3 @ idx digit result sidx value IF OVER 5. ≥ THEN 1. + SWAP @ digit idx+1 result sidx value 4. - SWAP @ idx+1 digit-4 result sidx value END sromt OVER ROT @ idx idx sromt digit result sidx value SUB @ sromt[] digit result sidx value ROT SWAP @ sromt[] result digit sidx value + SWAP 1. - @ digit-1 result sidx value END ROT @ sidx digit-1 result value DROP2 @ result value END UNTIL OVER NOT @ 0./1. result value END NIP @ result END » »
« IF DEPTH NOT THEN 1. 3999. END { } UNROT FOR y y DUP ROMAN ROMAN IF DUP ROT ≠ THEN y SWAP R→C + ELSE DROP END NEXT ZVONEK »
« 0. 19. .05 → period « START 1046.5 period BEEP 1318.51 period BEEP NEXT » »
Zjevně došlo k formální chybě: kapitola má nadpis "Zdrojový text" a ono jich je tam víc! Bystřejší z nás snadno určí, že počet zdrojových textů je zcela přesně roven číslu tři. Ano, došlo k pochybení, leč vylhat se z něj bude jistě efektnější, něž se k němu chlapsky přiznat. Nuže, provozní program je jenom jeden („...to je přece každýmu jasný, nééé?“) a jmenuje se ROMAN. Další je zkušební modul TROMAN pro ověření funkčnosti pro celý rozsah vstupních hodnot. Poslední v pořadí se jmenuje ZVONEK a slouží výhradně ku zvonění :-)
Download | ↑ ↓ |
Textový tvar | Binární forma |
---|---|
ROMAN | ROMAN.hp |