X36UNX – 16 Numerické výpočty v sh příkazy expr, bc, dc Zdeněk Sojka
[email protected]
dc – desk calculator - zadávání příkazů postfixově - data se ukládají do stacku - příkazy obyčejně pracují s jedním nebo dvěma operandy na vrcholu stacku - je možné napsat na jeden řádek více příkazů, nemusí ani být odděleny mezerami - bylo by to nepřehledné - není možné vkládat komentáře
Základní příkazy f – vypíše celý stack p – vypíše prvek z vrcholu zásobníku c – vyprázdní hlavní stack d – push vrcholu zásobníku – zdvojení prvku q – konec programu
Vkládání čísel - je možné vkládat v jakékoli soustavě se základem 2 – 16 - povolena desetinná tečka - záporná čísla se označují podtržítkem, například _1.65 - binární minus by způsobilo odečtení dvou operandů na vrcholu zásobníku - nezávisle na volbě soustavy je možné používat čísla 0-9 a písmena A-E FF vloží při bázi 10 číslo 165
Základní matematické operace - namísto dvou nejvyšších operandů vloží jejich funkci Sčítání
+
5 3 f # zadání dvou čísel a vypsání stacku 3 5 + f # součet a vypsat 8
Odčítání
-
5 3 - f 2
Násobení
*
5 3 * f 15
Dělení
/
5 3 / f 1 c 3 k 5 3 / f 1.666
# smazání stacku # k – nastavení přesnosti na 3 řády
Modulo
%
5 3 % f 2
Celočíselná mocnina ^ 5 3 ^ f 125 c 5 3.5 ^ f exp not an integer
Odmocnina v 256 v f 16 _16 v # minus 16 sqrt of neg number
Práce s dočasnými registry - fungují jako stack - 256 registrů dostupné pod znaky ASCII - nejlépe se přistupuje k těm, které mají označení 0-9, a-z, A-Z, mezera, enter Push vrcholu zásobníku do registru Sx Pop vrcholu registru do zásobníku Lx - je-li registr prázdný, operace se neprovede Přepsání vrcholu registru sx Kopie vrcholu registru na zásobník lx - je-li registr prázdný, vrací 0
Příklad – Sx, Lx, sx, lx 3 Sa 4 Sa 5 sa f empty stack # hodnota 3 se přesunula do registru a, 4 také, ale pak byla přepsána hodnotou 5 la La La la f 0 # hodnota z posledního la, kdy byl registr prázdný 3 # La, hodnota uložená prvním Sa 5 # La, tedy pop vrcholu registru # číslo 4 bylo přepsáno pomosí sa 5 # la, okopírování vrcholu registru La L? # chyba, registr je prázdný f 0 # stack se nezměnil 3 5 5
Práce s číselnými soustavami Nastavení číselné soustavy pro vstup i 16 i f empty stack # stack je prázdný, hodnota se popnula 11 i # 17 v desítkové soustavě! input base is too large F0 f 240 f0 f # zadáno malým písmem – je to příkaz ~ f 0 f 240 # první f 0 # druhé f, uložená 0 240
Push číselné soustavy vstupu
I
I f 10
# defaultně desítková soustava
Nastavení soustavy pro výpis
o
5 o f empty stack 123456789 # velké číslo na vrchol stacku 16 o f # vypsat v šestnáctkové soustavě 75BCD6F 30 o f # při větším základu se už nepoužívaj písmena 05 02 12 14 09 09 100 o f output base is too large 05 02 12 14 09 09 # soustava se nezměnila 99 o f # nejvyšší soustava je 99ková 01 28 23 35 18
Push výstupní soustavy 5 o O f 10 10 o f 5
O
# nastavení pětkové soustavy # základ vypadá vždy jako 10 # desítková soustava pro výpis # v desítkové je vidět, že tam byla 5
Různá přesnost Nastavení přesnosti výpočtu k 50 k 2 v f # odmocnina ze 2 na 50 míst 1.41421356237309504880168872420969807856967187537694 0 k 2.000 v f # nulová přesnost, ale zadáno číslo se třemi místy 1.414 # přesnost se zachovala
Zjištění přesnosti výpočtu K K f 0 # defaultně se počítá jen s celými čísly
Řetězce Vložení řetězce do stacku
[…]
[abcdef] f abcdef
Výpis řetězce z vrcholu stacku a jeho smazání
P
[abcdef] P f abcdefempty stack
Spuštění příkazu na vrcholu zásobníku [2.000 v f] x 1.414
x
# uložení řetězce a jeho spuštění
Porovnání a podmíněné spuštění příkazu z vrcholu registru
>r
[2.000 v f c] sa f # příkaz do registru a empty stack 1 2 >a # push 1,2,porovnání,vykonání příkazu a !není mezera! 1.414 1 2
Příklady Výpis faktoriálu 1...5 (man) [la 1 + d sa * p la 5 >y] sy 0 sa 1 ly x
# # # #
načtení registru a, zvýšení o 1, uložení kopie zpět do a vynásobení hodnoty na stacku a výpis načtení a, porovnání s 5 – pokud je menší, znovu se spustí program v registru y # uložení do registru y # 0 -> a, 1 na stack # načtení y a spuštění
Celý kód (pro zkopírování do dc): [la1+dsa*pla5>y]sy 0sa1 lyx 1 2 6 24 120
Geometrická posloupnost 1.25 sa # a0 1.8 sq # kvocient 5 sn # počet čisel 5 k # přesnost práce la # načtení na stack [p # výpis lq * # vynásobení kvocientem ln 1 # načtní n, snížení o 1 d sn # uložení kopie zpět do n, druhé číslo zůstává na stacku 0
Celý kód (pro zkopírování do dc) 1.25 sa 1.8 sq 5 sn 5 k la [p lq * ln 1 - d sn 0
Výpočet funkce sinus 1
3
5
7
n
2⋅n1
∞ −1 ⋅x x x x x sin x = − − ⋯=∑ 1! 3! 5! 7! 2⋅n1 ! n=0
1.000 sx 6 sy 20 k
# stupeň Taylorova polynomu # přesnost výstupu
lx d sa # iniciální hodnoty, dočasná hodnota sin na stacku 1 d sc sb 0 se [la lx * lx * _1 * d sa lb lc 1 + d sc * lc 1 + d sc * d sb / + p le 1 + d se ly >f] sf lf x # spuštění
Detailnější popis funkce [la lx * lx * _1 * d sa # a = čitatel zlomku = a * x * x * (-1) lb lc 1 + d sc * lc 1 + d sc * d sb # b = jmenovatel zlomku, # faktoriál = b * (c + 1) * (c + 2) # c = aktuální argument faktoriálu / # vydělení čitatele a jmenovatele + # přičtení k hodnotě na vrcholu zásobníku p # vypsání aktuální iterační hodnoty le 1 + d se # zvýšení iterace ly >f] # porovnání s y a případné spuštění f sf # uložení celého řetězce do f
Příklad pro okopírování do dc 0.123 sx 6 sy 20 k lx d sa 1 d sc sb 0 se [la lx * lx * _1 * d sa lb lc 1 + d sc * lc 1 + d sc * d sb / + p le 1 + d se ly >f] sf lf x c .12268985550000000000 .12269009010880702500 .12269009002429758116 .12269009002431533870 .12269009002431533626 .12269009002431533626
bc Program rozšiřuje možnosti dc, který je používán jako backend. Parametry bc [-l] [-c] [soubor] -c -l
zobrazování příkazů dc namísto jejich spouštění nahraje „knihovní funkce“ (sin s(x), cos c(x), exp e(x), ln l(x), atn a(x) ) a nastaví rozšířenou přesnost pokud je i přepínač -c, zobrazí se nahrávání těchto programů v „jazyce“ dc. Jedná se tedy o nahrání funkcí do některých registrů soubor načítá vstup ze souboru, po jeho dokončení čte ze stdin
Syntaxe příkazů - obdoba jazyka C, zjednodušení - pouze jednoznakové proměnné, malá abeceda if ( x==6 ) { y=7 z=8 } while (x<7) x++
a…z
# pokud x==6, nastaví se proměnné # zvýší a vypíše x
while (x<7) x=x+1 # nevypisuje x for (i = 0; i <= 3; i+=0.5) s(i) #vypíše hodnoty sinus 0…3 define f(x) { auto a a=x y=x return(x) }
# funkce, předávání hodnotou # deklarace proměnné # nastaví se pouze lokální proměnná # globální proměnná # navrácení hodnoty
Nastavení proměnné z příkazové řádky > bash > set -x > a=$(echo "sqrt(2)" | bc -l) ++ echo 'sqrt(2)' ++ bc -l + a=1.41421356237309504880 > a=$(echo $a*"sqrt(2)" | bc -l) ++ echo '1.41421356237309504880*sqrt(2)' ++ bc -l + a=1.99999999999999999999 > sh > set -x > a=`echo "sqrt(2)" | bc -l` + + echo sqrt(2) bc -l a=1.41421356237309504880 > a=`echo $a*"sqrt(2)" | bc -l` + bc + echo 1.41421356237309504880*sqrt(2) -l a=1.99999999999999999999
expr - vyhodnocování jednoduchých výrazů - problémy s náhradou na příkazové řádce - operandy je nutné oddělovat mezerou Manuál man -s 2 expr
Příklady – sh > sh > set -x > a=7 > a=`expr $a + 1` # obrácené apostrofy ! + expr 7 + 1 a=8 > a="expr $a + 1" a=expr 8 + 1 # a nastaven na řetězec > a='expr $a + 1' a=expr 8 + 1 # také řetězec
Příklady – bash > bash > set -x > a=1 > a=$(expr $a + 1) # toto nejde v sh ++ expr 1 + 1 + a=2 > expr 2 \* 2 # pozor na nahrazení ! + expr 2 '*' 2 4 > expr \( 1 \| 2 \) + expr '(' 1 '|' 2 ')' 1 # true > expr \( 1 \| 1 \) \& 0 # už to začíná být nepřehledné... + expr '(' 1 '|' 1 ')' '&' 0 0 # false
Úloha 16 Napište příkaz pro program dc, který vytvoří program na zobrazení n (n>=2) prvků Fibonacciho posloupnosti. Parametr pak bude v registru n, může se měnit. Funkce musí pracovat rekurzivně, ne použitím vzorce. Program bude uložen v registru f. Je očekáváno chování: 5 sn lf x 1 1 2 3 5
Návod (doporučení) - první dvě čísla vypište bez počítání - je dobré mít dva registry, v jednom vždy větší číslo, ve druhém menší - můžete donutit funkci modifikovat sama sebe - nejdříve nastaví registry a b, vypíše 1 1 a pak přepíše registr f, který spustí - příklad [1 d p p sa sb [ … kód funkce … ln 1 - d sn 2
(zde „kód funkce“ skoro už jen sčítá a vypisuje čísla)