Hlavní navigace

Matematika v příkazovém řádku V - nástroj dc (2)

21. 2. 2006
Doba čtení: 11 minut

Sdílet

V pátém pokračování seriálu věnovaného programovým nástrojům určeným pro matematické výpočty prováděné zejména v příkazovém řádku dokončíme popis utility dc. Budeme se zabývat použitím proměnných a také systémem pro zpracování řetězců, které se v dc používají zejména pro programování maker.

Obsah

1. Zásobníky a proměnné v dc
2. Práce s řetězci
3. Operace prováděné s řetězci
4. Základy tvorby maker
5. Tvorba pokročilejších maker
6. Soupis všech popisovaných příkazů
7. Obsah dalšího pokračování tohoto seriálu

1. Zásobníky a proměnné v dc

V předchozím pokračování tohoto seriálu jsme si vysvětlili základní způsob ovládání aplikace dc, spolu s významem zásobníků a postfixové notace (označované také RPN neboli Reverse Polish Notation) při zápisu aritmetických výrazů. V dnešním pokračování dokončíme popis utility dc. Ukážeme si, jakým způsobem je možné pracovat s řetězci a jak se řetězce používají při tvorbě pokročilejších maker, včetně vytváření podmíněných příkazů a cyklů (ty je nutné implementovat rekurzí). Nejprve si však vysvětleme způsob práce s proměnnými.

Na proměnné se můžeme v utilitě dc dívat ze dvou stran. Na jednu stranu se proměnné chovají v mnoha ohledech podobně jako v jiných programovacích jazycích, tj. obsahují jedinou numerickou či řetězcovou hodnotu – dc si přitom datový typ uložené hodnoty pamatuje. Na druhou stranu však může být každá proměnná využita jako pojmenovaný zásobník, což má velký význam, zejména proto, že proměnných může být teoreticky použito pouze 256, protože jsou pojmenovány pouze jedním ASCII znakem – většinou se však používá pouze 26 proměnných, což odpovídá počtu písmen anglické abecedy. Vzhledem k faktu, že se každá proměnná chová jako samostatný zásobník, je možné jejich využití například v makrech bez toho, aby se obsah proměnné změnil (samozřejmě za předpokladu, že makro korektně odstraní všechny mezivýsledky výpočtů). Pro práci s proměnnými jsou určeny následující operace:

Práce s proměnnými
Kód příkazu (operace) Význam příkazu
s[název proměnné] Vyjmutí numerické či řetězcové hodnoty z vrcholu zásobníku operandů a uložení této hodnoty v proměnné zadané svým názvem.
l[název proměnné] Přečtení hodnoty z proměnné zadané svým názvem a uložení této hodnoty na vrchol zásobníku operandů.
S[název proměnné] Vyjmutí numerické či řetězcové hodnoty z vrcholu zásobníku operandů a uložení této hodnoty na vrchol zásobníku zadaného svým názvem. Proměnná se zde tedy chová jako zásobník.
L[název proměnné] Vyjmutí posledně vložené hodnoty ze zásobníku zadaného svým jménem a uložení této hodnoty na vrchol zásobníku operandů.

V souvislosti s výše uvedenými operacemi stojí za povšimnutí jednak odlišnost chování proměnných při provádění různých operací a také již minule zmíněná závislost na velikosti písmen operací. Další operace s  proměnnými přímo provádět nelze; pokud je například zapotřebí pracovat s hodnotami uloženými hlouběji v zásobnících, je nutné provést jejich přesun na zásobník operandů. Také kopírování či přesun hodnot mezi jednotlivými proměnnými se děje přes zásobník operandů.

Práci s proměnnými si můžeme ukázat na následujících jednoduchých příkladech. Podobně jako v minulé části, i zde budou vstupy od uživatele zapsány tučným písmem:

# pokus o zápis tří hodnot do proměnné 'a' pomocí operace 's':
1sa
2sa
3sa
# zásobník operandů je prázdný - důkaz:
f
# načtení hodnoty proměnné 'a' pomocí operace 'l':
la
f
3
la
f
3
3
la
f
3
3
3
# proměnná se při těchto operacích chová jako "jednohodnotová" proměnná
# (na zásobník operandů je třikrát po sobě zapsáno jedno číslo)
q 

Při operacích S a L však bude situace odlišná:

# pokus o zápis tří hodnot do proměnné 'a' pomocí operace 'S':
1sa
2sa
3sa
# zásobník operandů je prázdný - důkaz:
f
# načtení hodnoty proměnné 'a' pomocí operace 'L':
La
f
3
La
f
2
3
La
f
1
2
3
# proměnná se při těchto operacích chová jako pojmenovaný zásobník
# (na zásobník operandů jsou zapsány tři odlišné hodnoty)
q 

2. Práce s řetězci

Utilita dc obsahuje i několik instrukcí pro práci s řetězci. Řetězce mohou být uloženy jak na zásobník operandů, tak i do libovolné proměnné pomocí výše uvedených čtyř operací. S řetězci je prakticky možné provádět pouze dvě řetězcové operace. První operací je tisk řetězce na standardní výstup, čehož se často používá například při interaktivní práci s nějakým skriptem nebo pro výpis chyb, které nastaly při výpočtu. Druhou operací je provedení řetězce jako makra, tj. znaky v řetězci jsou chápány jako příkazy jazyka dc a jsou po zadání některého „spouštěcího“ příkazu provedeny. Speciální operací je konstruktor řetězce, což je vlastně příkaz sestávající ze dvou znaků pro levou a pravou hranatou závorku, přičemž všechny znaky, které se nachází uvnitř těchto závorek, jsou považovány za součást řetězce. Operace pro práci s řetězci budou podrobněji popsány v dalších podkapitolách.

3. Operace prováděné s řetězci

Mezi základní operace, které je možné s řetězci provádět, patří:

Operace s řetězci
Kód příkazu (operace) Význam příkazu
[] Znaky, které jsou umístěné mezi oběma závorkami, jsou chápány jako řetězec, který je vložen na vrchol zásobníku operandů. Ve skutečnosti se tedy jedná o zápis konstruktoru řetězce.
a Pokud je na vrcholu zásobníku operandů uloženo číslo, je na TOS po provedení této operace uložen jeho první byte jako řetězec. Pokud je na vrcholu zásobníku operandů uložen řetězec, je zpět vložen jeho první znak. Původní POSIXová verze utility dc tento příkaz neobsahuje.
p Tisk numerické či řetězcové hodnoty uložené na vrcholu zásobníku operandů (TOS – Top Of Stack) bez vyjmutí této hodnoty ze zásobníku. Za vytištěnou hodnotu je přidán znak pro konec řádku.
n Tisk numerické či řetězcové hodnoty uložené na vrcholu zásobníku operandů spolu s vyjmutím hodnoty ze zásobníku. Za vytištěnou hodnotu není přidán znak pro konec řádku.
f Postupný tisk všech numerických či řetězcových hodnot uložených na vrcholu zásobníku operandů. Každá hodnota je vytištěna na samostatný řádek. Hodnoty jsou na zásobníku ponechány.

Následuje velmi jednoduchý příklad práce s řetězci:

[Hello world]
p
Hello world
q 

Využití proměnných a příkazu n pro tisk bez vložení znaku pro konec řádku (opticky dochází ke spojení tří řetězců, ve skutečnosti však taková operace není podporována):

# Naplnění proměnných 'a', 'b' a 'c'
[Hello]sa
[ ]sb
[world]sc
# Přesun hodnot proměnných 'a', 'b' a 'c' na zásobník operandů
lclbla
# Tisk tří hodnot na zásobníku operandů bez odřádkování
nnn
Hello world
# Ukončení aplikace
# (ve skutečnosti je následující znak umístěn za vytištěný řetězec)
q 

4. Základy tvorby maker

Jak již bylo řečeno ve třetí kapitole, je možné řetězce použít pro zápis maker. V tomto případě se využívají následující operace:

Operace pro práci s makry
Kód příkazu (operace) Význam příkazu
x Pokud je na vrcholu zásobníku operandů uložen řetězec, je chápán jako makro, které je ihned provedeno.
>[název proměnné] Porovná a odstraní dvě hodnoty uložené na vrcholu zásobníku operandů. Pokud je hodnota v TOS větší, provede se kód uložený v proměnné zadané svým názvem.
!>[název proměnné] Podobné předchozí operaci s tím, že se makro provede, pokud je TOS menší nebo rovno druhé hodnotě.
<[název proměnné] Spuštění makra, pokud je TOS menší než druhá hodnota.
!<[název proměnné] Spuštění makra, pokud je TOS větší nebo rovno druhé hodnotě.
=[název proměnné] Spuštění makra v případě, že jsou si obě hodnoty uložené na vrcholu zásobníku operandů rovny.
!=[název proměnné] Spuštění makra v případě, že si hodnoty nejsou rovny.
q Ukončení běhu makra, včetně makra volajícího.
Q Ukončení n volání maker, přičemž hodnota n je uložena na vrcholu zásobníku.
? Načtení řetězce ze standardního vstupu a spuštění tohoto řetězce jako makra.

Při tvorbě maker musíme dávat pozor na to, že samotný „skript“ makra je řetězec, musí tedy být umístěn v hranatých závorkách. To je uvedeno na následujícím jednoduchém příkladu, který vypíše, zda je číslo uložené na zásobníku nulové. Skript je uložen v proměnné z (jako „zero“), text s příkazem pro tisk nulové hodnoty v proměnné t (jako „text“) a konečně text s příkazem pro tisk nenulové hodnoty v proměnné n (jako non-zero):

# příkaz pro tisk prvního řetězce je uložen jako makro do proměnné 't'
[[na zasobniku je nula]n]st
# příkaz pro tisk druhého řetězce je uložen jako makro do proměnné 'n'
[[na zasobniku je nenulova hodnota]n]sn
# makro provádějící dva testy - na rovnost nuly a na nerovnost nuly
# (všimněte si duplikace původní hodnoty, první test by tuto hodnotu zničil)
# toto makro je uloženo do proměnné 'z'
[d0=t0!=n]sz
# vyzkoušení funkce makra
10lzx
0lzx 

Jak je z předchozího zápisu patrné, je tvorba maker určena pouze pro otrlé, a to jste ještě neviděli příklady uvedené v následující kapitole :-)

5. Tvorba pokročilejších maker

V této kapitole jsou ukázána složitější makra, která byla vytvořena pokročilými uživateli utility dc.

Fibonacciho posloupnost:

1 sa
1 sb
2 sc
[la lb + p lb sa sb lc 1 + d sc 13 >z] sz
la p sx lp p sx lz x 

Výpočet největšího společného dělitele dvou čísel:

?[dSarLa%d0<a]dsax+p 

6. Soupis všech popisovaných příkazů

V následující tabulce jsou uvedeny všechny popisované příkazy, které je možné v utilitě dc provádět. Existují i rozšířené verze dc, které obsahují další příkazy, zde popisovaná podmnožina by však měla korektně pracovat na všech operačních systémech a platformách vyhovujících standardu POSIX:

CS24_early

Příkazy
Kód příkazu Význam příkazu
q Speciální příkaz, který slouží k okamžitému ukončení běhu utility dc.
# Speciální příkaz, po jehož nalezení se ignorují všechny znaky až do konce řádku. Tento příkaz je typicky použit pro komentáře.
c Vyjmutí všech hodnot ze zásobníku operandů; po provedení tohoto příkazu je zásobník ponechán prázdný.
d Poslední vložená položka na zásobníku operandů je zduplikována, počet položek se tedy o jednotku zvýší. Jedná se o obdobu Forthovského příkazu „dup“.
r Výměna dvou položek na zásobníku operandů: položky uložené nejvýše (TOS) a položky uložené těsně pod TOS. Pro korektní provedení této operace musí být na zásobníku alespoň dvě položky. Jedná se o obdobu Forthovského příkazu „rot“.
p Tisk numerický či řetězcové hodnoty uložené na vrcholu zásobníku operandů (TOS – Top Of Stack) bez vyjmutí této hodnoty ze zásobníku. Za vytištěnou hodnotu je přidán znak pro konec řádku.
n Tisk numerické či řetězcové hodnoty uložené na vrcholu zásobníku operandů spolu s vyjmutím hodnoty ze zásobníku. Za vytištěnou hodnotu není přidán znak pro konec řádku.
f Postupný tisk všech numerických či řetězcových hodnot uložených na vrcholu zásobníku operandů. Každá hodnota je vytištěna na samostatný řádek. Hodnoty jsou na zásobníku ponechány.
s[název proměnné] Vyjmutí numerické či řetězcové hodnoty z vrcholu zásobníku operandů a uložení této hodnoty v proměnné zadané svým názvem.
l[název proměnné] Přečtení hodnoty z proměnné zadané svým názvem a uložení této hodnoty na vrchol zásobníku operandů.
S[název proměnné] Vyjmutí numerické či řetězcové hodnoty z vrcholu zásobníku operandů a uložení této hodnoty na vrchol zásobníku zadaného svým názvem – proměnná se zde tedy chová jako zásobník.
L[název proměnné] Vyjmutí posledně vložené hodnoty ze zásobníku zadaného svým jménem a uložení této hodnoty na vrchol zásobníku operandů.
+ Vzájemný součet dvou hodnot uložených na nejvyšších místech zásobníku operandů. Obě hodnoty jsou ze zásobníku vyjmuty, přičemž se do TOS uloží výsledek operace.
- Vzájemný rozdíl dvou hodnot uložených na nejvyšších místech zásobníku operandů. Obě hodnoty jsou ze zásobníku vyjmuty, přičemž se do TOS uloží výsledek operace.
* Vzájemné vynásobení dvou hodnot uložených na nejvyšších místech zásobníku operandů. Obě hodnoty jsou ze zásobníku vyjmuty, přičemž se do TOS uloží výsledek operace.
/ Vzájemný podíl dvou hodnot uložených na nejvyšších místech zásobníku operandů. Obě hodnoty jsou ze zásobníku vyjmuty, přičemž se do TOS uloží výsledek operace.
% Výpočet zbytku po dělení (dělení modulo) dvou hodnot uložených na vrcholu zásobníku operandů. Obě hodnoty jsou ze zásobníku vyjmuty, přičemž se do TOS uloží výsledek operace.
~ Současné vydělení a výpočet zbytku po dělení dvou hodnot uložených na vrcholu zásobníku operandů. Obě hodnoty jsou ze zásobníku vyjmuty, přičemž se do TOS uloží výsledek operace.
^ Výpočet celočíselné mocniny. Jak hodnota, jež se má umocnit, tak i mocnina jsou uloženy na zásobníku operandů. Obě hodnoty jsou ze zásobníku vyjmuty, přičemž se do TOS uloží výsledek operace.
v Výpočet druhé odmocniny z hodnoty, jež je uložena na vrcholu zásobníku operandů.
k Specifikace radixu, tj. počtu desetinných míst pro provádění a tisk výsledků aritmetických operací. Hodnota radixu musí být uložena na vrcholu zásobníku operandů.
i Specifikace číselné soustavy použité při zadávání (načítání) numerických hodnot. Základ číselné soustavy musí být uložen na vrcholu zásobníku operandů.
o Specifikace číselné soustavy použité při tisku (výstupu) numerických hodnot. Základ číselné soustavy musí být uložen na vrcholu zásobníku operandů.
[] Znaky, které jsou umístěné mezi oběma závorkami, jsou chápány jako řetězec, který je vložen na vrchol zásobníku operandů.
a Pokud je na vrcholu zásobníku operandů uloženo číslo, je na TOS uložen jeho první byte jako řetězec. Pokud je na vrcholu zásobníku operandů uložen řetězec, je zpět vložen jeho první znak. Původní POSIXová verze utility dc tento příkaz neobsahuje.
x Pokud je na vrcholu zásobníku operandů uložen řetězec, je chápán jako makro, které je ihned provedeno.
>[název proměnné] Porovná a odstraní dvě hodnoty uložené na vrcholu zásobníku operandů. Pokud je hodnota v TOS větší, provede se kód uložený v proměnné zadané svým názvem.
!>[název proměnné] Podobné předchozí operaci s tím, že se makro provede, pokud je TOS menší nebo rovno druhé hodnotě.
<[název proměnné] Spuštění makra, pokud je TOS menší než druhá hodnota.
!<[název proměnné] Spuštění makra, pokud je TOS větší nebo rovno druhé hodnotě.
=[název proměnné] Spuštění makra v případě, že jsou si obě hodnoty uložené na vrcholu zásobníku operandů rovny.
!=[název proměnné] Spuštění makra v případě, že si hodnoty nejsou rovny.
q Ukončení běhu makra, včetně makra volajícího.
Q Ukončení n volání maker, přičemž hodnota n musí být uložena na vrcholu zásobníku.
? Načtení řetězce ze standardního vstupu a spuštění tohoto řetězce jako makra.

7. Obsah dalšího pokračování tohoto seriálu

V dalším, již šestém pokračování tohoto seriálu si popíšeme aplikaci calc, což je, mimo jiné, i plnohodnotný aritmetický kalkulátor vytvořený tak, aby ho bylo možné ovládat z textového uživatelského rozhraní, tj. buď z plnohodnotného terminálu nebo z příkazového řádku. Narozdíl od dnes popisované utility dc je ovládání calcu pro mnohé uživatele lidštější, na druhou stranu není calc obsažen ani v normě POSIX ani v mnoha Linuxových distribucích, proto může být jeho použití (volání) v dále šířených skriptech problematické a obecně nepřenositelné.

Byl pro vás článek přínosný?

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.