Matematika v príkazovom riadku

17.06.2013 20:11 | Články | mirek biňas
Matematika v príkazovom riadku
Medzi základné znalosti správneho linuxáka rozhodne patrí znalosť príkazového riadku. Čím častejšie sa mu venuje, tým väčšiu skúsenosť postupne získava. Pokiaľ však potrebuje v príkazovom riadku niečo vypočítať, môže naraziť na problém. Bash síce podporuje aritmetické výrazy, avšak so značným obmedzením - keď totiž budete chcieť počítať s desatinnými číslami, vaše snaženie skončí so syntaktickou (!) chybou (schválne vyskúšajte napísať echo $((1.1+1))). V tomto článku sa teda každý správny linuxák zoznámi so základmi používania „kalkulačky“ s názvom bc (ak ju náhodou ešte nestihol spoznať ;)

Kalkulačka v príkazovom riadku?

V prvom rade treba povedať, že bc nie je len kalkulačka. Jedná sa totiž o jazyk, ktorý podporuje ľubovoľnú (arbitrary) presnosť čísiel. Okrem toho obsahuje aj interaktívny režim, v ktorom je možné vykonávať príkazy (podobne ako je tomu v bash-i alebo v Python-e). Jeho syntax je podobná syntaxy jazyka C, pozná základné funkcie na prácu s riadením toku programu, je v 8;om možné pracovať s premennými, definovať vlastné funkcie (programovateľná kalkulačka) a pod. Taktiež je možné k nemu pri spustení pripojiť aj matematickú knižnicu, pomocou ktorej je možné jeho funkcionalitu ešte viac rozšíriť. Keď ho spustíte z príkazového riadku bez akýchkoľvek parametrov, spustí sa interaktívny režim, kedy môžete priamo vykonávať (resp. spúšťať) jeho príkazy. V opačnom prípade začne prechádzať a spracovávať súbory, ktoré sú mu odovzdané ako parametre. Keď skončí so spracovaním súborov, začne načítavať vstup zo štandardného vstupu. Je ho však rovnako možné použiť na výpočty vo vlastných skriptoch. O tom ale trošku neskôr. Začneme pomaly.

Poznámka na úvod: Tento článok je napísaný pre verziu bc 1.06.95, ktorá sa nachádza v Ubuntu 12.10. Podľa manuálovej stránky je táto verzia založená na drafte POSIX P1003.2/D11, avšak oproti tomuto draftu a tradičným implementáciám obsahuje táto verzia niekoľko odlišností a rozšírení.

Prvé kroky v interaktívnom režime

Ako sa používa kalkulačka, netreba zbytočne hovoriť. Tú našu treba spustiť v príkazovom riadku pomocou príkazu bc. Tým sa dostaneme do interaktívneho režimu, kedy vieme zadávať potrebné výrazy (a príkazy) pre spracovanie. Jednoduchý výpočet po spustení interaktívneho režimu je znázornený na nasledujúcom príklade:
$ bc
bc 1.06.95
Copyright 1991-1994, 1997, 1998, 2000, 2004, 2006 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
127+33*(21/7)
226
a=2
a^3
8
Pokiaľ nechcete, aby jazyk bc po spustení vypisoval všetky uvedené reťazce, spustite ho s prepínačom -q, pomocou ktorého zapnete tichý režim.

V jazyku bc je možné písať a používať rovnaké operátory, aké podporuje jazyk C. Okrem štandardných aritmetických operátorov *, /, -, +, %, ^ je možné obsah premennej inkrementovať a dekrementovať pomocou operátorov ++ a -- a to prefixným aj postfixným spôsobom. Rovnako tak je možné priradiť premennej výsledok výrazu formou var = expr, napríklad sucet += 5. Vo výpočtoch je možné taktiež použiť štandardnú funkciu sqrt(expr) na získanie druhej odmocniny z výsledku výrazu expr.

Vlastnosti zobrazovaných čísiel

Ak ste skúsili riešiť komplikovanejšie výrazy, zrejme ste narazili na problém súvisiaci s desatinnými číslami. Ak sa totiž pokúsite vypočítať výraz, ktorého výsledkom má byť desatinné číslo, budete sklamaní vypísaním len celočíselnej hodnoty výsledku (vyskúšajte napr. napísať výraz 9/2). A to som na začiatku avizoval, že práve bc tento nedostatok bash-u vyrieši. V čom je teda problém?

Všetky čísla sú interne reprezentované desiatkovo. Rovnako aj samotný výpočet je následne realizovaný v desiatkovej sústave. Každé číslo má dva atribúty, ktorými sú dĺžka (length) a škála (scale). Dĺžka je definovaná ako celkový počet desiatkových číslic v čísle a škála je definovaná ako celkový počet číslic za desatinnou čiarkou. Uveďme si príklad:
  • číslo .000123 má dĺžku 6 a škálu 6
  • číslo 123.4567 má dĺžku 7 a škálu 4
Ak chcete zistiť dĺžku čísla, môžete na to využiť funkciu length(), napríklad:
lenght(123.456)
6
Ak chcete zistiť škálu čísla, môžete na to využiť funkciu scale(), napríklad:
scale(123.456)
3
Nás však bude zaujímať hodnota špeciálnej premennej scale, ktorá je aktuálne používaná jazykom pre zobrazovanie výsledku výpočtu. To je možné zistiť vypísaním obsahu premennej scale:
scale
0
Problém, prečo sme pri delení dostávali len celočíselné výsledky, by mal byť teda jasný – je potrebné správne nastaviť hodnotu premennej scale. To docielime jednoduchým priradením potrebnej hodnoty do tejto premennej v závislosti od požadovanej presnosti výpočtu. Uvediem príklad:
9/2
4
scale=3
9/2
4.500
1/3
.333
Ako bolo uvedené na začiatku, vlastnosťou jazyka bc je, že podporuje čísla s ľubovoľnou presnosťou. Využitie tejto vlastnosti závisí práve od hodnoty špeciálnej premennej scale. Ukázať si to môžeme na výpočte čísla PI (funkcia a() v príklade predstavuje arkus tangens a je ju možné použiť pri spustení bc s prepínačom -l (s matematickou knižnicou)):
scale=100
4*a(1)
3.141592653589793238462643383279502884197169399375105820974944592307\
8164062862089986280348253421170676
Vyskúšajte si v príkazovom riadku spustiť nasledujúci príkaz, ktorý na vašom počítači vyráta hodnotu PI s presnosťou na 10000 miest a zobrazí čas, ktorý je potrebný na získanie výsledku. Z príkladu by teda malo byť jasné, že čím väčšiu presnosť budete vyžadovať, tým dlhšie bude samotný výpočet trvať. Časť výsledku výpočtu čísla PI na mojom systéme prikladám vo výpise:
$ time echo "scale=10000; 4*a(1)" | bc -l -q
3.141592653589793238462643383279502884197169399375105820974944592307\
…
79310657922955249887275846101264836999892256959688159205600101655256\
375676
real 4m20.352s
user 4m20.028s
sys 0m0.084s

Špeciálne a vlastné premenné

Ako bolo možné vidieť v predchádzajúcom príklade, bola použitá premenná scale, pomocou ktorej je možné nastaviť presnosť výsledku výpočtu. Táto premenná patrí medzi zabudované premenné jazyka bc, ktoré sa v manuálovej stránke označujú priamo ako špeciálne premenné.

Jazyk bc pozná tieto štyri špeciálne premenné:
  1. scale – Hodnota premennej definuje, koľko číslic bude zobrazených za desatinnou čiarkou po výpočte bez zaokrúhlenia. Predvolená hodnota tejto premennej je 0.
  2. ibase – Hodnota premennej definuje základ číselnej sústavy, ktorá bude použitá pre vstupné hodnoty. Predvolená hodnota tejto premennej je 10.
  3. obase – Rovnako ako iba, ale pre výstupné hodnoty.
  4. last – V tejto premennej sa vždy nachádza posledné vypísané číslo. Po spustení jazyka má táto premenná samozrejme hodnotu 0. Pri výpočtoch môže byť nahradená znakom bodka '.'. Pre zvýšenie čitateľnosti výrazu však odporúčam používať samotnú premennú.
Všetky tieto premenné je možné použiť vo vlastných výrazoch a samozrejme je možné ich hodnotu podľa potreby aj nastaviť.

Okrem špeciálnych premenných je možné používať aj vlastné premenné. Tieto netreba nijakým spôsobom deklarovať, ale stačí ich priamo definovať priradením hodnoty. Každá vlastná premenná musí byť pomenovaná a pre pomenovanie sú použité rovnaké pravidlá, ako pre premenné v jazyku C (premenná musí začínať písmenom, ktoré je nasledované ľubovoľným počtom písmen, číslic a znaku podtržník '_'). Jedniný rozdiel oproti jazyku C je ten, že názvy premenných nesmú (!) obsahovať veľké písmená.

Jazyk bc rozoznáva dva typy premenných – jednoduché premenné a polia. Typ premennej je rozpoznaný automaticky z kontextu, keďže každé pole je ohraničené hranatými zátvorkami. Príklad použitia premenných nasleduje:
index=7
array[index]=2
array[0]
0
array[7]
2
result=index^array[2*3+1]
Len pre kontrolu overenia vašej sústredenosti - viete povedať, akú hodnotu bude mať premenná last po vykonaní posledného riadku príkladu?

Výpočty v rôznych číselných sústavách

Ako bolo uvedené v predchádzajúcej časti, je možné použiť špeciálne premenné ibase a obase na zmenu číselnej sústavy pre zadávanie vstupných hodnôt a pre vypisovanie výsledných hodnôt z jednotlivých operácií. Túto vlastnosť je možné prezentovať na využití jazyka bc ako jednoduchého nástroja na prevod čísel medzi číselnými sústavami.

Ak teda pracujeme v desiatkovej sústave a chceme čísla z desiatkovej sústavy prevádzať do sústavy napríklad dvojkovej, docielime to nastavením špeciálnej premennej obase na hodnotu 2:
obase=2
16
10000
Pokiaľ chceme overiť správnosť výsledku a previesť číslo z dvojkovej do desiatkovej sústavy, je potrebné nastaviť hodnotu premennej ibase na 2 (a v tomto prípade aj hodnotu obase späť na 10):
obase=10
ibase=2
10000
16
Tu si však dajte rovnako pozor na to, že od momentu, keď zmeníte hodnotu ibase na sústavu inú ako desiatkovú, musíte zadávať všetky hodnoty v tejto sústave. Ináč môžete byť prekvapení na pohľad logickým postupom, ale „nesprávnym“ výsledkom (aj) predchádzajúceho príkladu:
ibase=2
obase=10
10000
10000

Použitie matematickej knižnice

Jazyk bc je možné rozšíriť o matematickú knižnicu, čím je možné získať prístup ku nasledujúcim funkciám:
  • s(x) – sínus výrazu x, pričom x je v radiánoch
  • c(x) – kosínus výrazu x, pričom x je v radiánoch
  • a(x) – arkus tanges výrazu x, pričom x je v radiánoch
  • l(x) – prirodzený logaritmus výrazu x
  • e(x) – exponenciálna funkcia e^x
  • j(n,x) – Besselova funkcia
Ak chcete spustiť bc s podporou matematickej knižnice, použite pri spustení prepínač -l. Hodnota premennej scale bude vtedy automaticky nastavená na 20.

Využitie bc vo vlastných skriptoch

Jazyk bc je výhodné použiť všade tam, kde nestačí bash so svojou celočíselnou aritmetikou. Integrovať ho do svojich skriptov je pritom naozaj veľmi jednoduché. Toto použitie ilustruje nasledujúci príklad:
$ echo 'scale=25;57/43' | bc
1.3255813953488372093023255
$ RESULT=$(echo 'obase=16;255' | bc)
$ echo "255 in hexa is $RESULT"
255 in hexa is FF
Samozrejme je možné si vo svojom bash skripte urobiť aj pomôcku vo forme funkcie, ktorá bude potrebný výraz posúvať jazyku bc. Jednoduchá podoba takejto funkcie a príklad jej využitia vo vlastnom skripte je ilustrovaný v nasledujúcom príklade, pomocou ktorého je možné vypočítať korene kvadratickej rovnice (bez kontroly správnosti vstupných parametrov):
#!/bin/bash

function evaluate(){
   if [ "$#" -eq 2 ]; then
      echo "scale=$1;$2" | bc
   else
      echo "scale=5;$1" | bc
   fi
}

echo "Zadaj kvadraticky clen a: "
read A
echo "Zadaj linearny clen b: "
read B
echo "Zadaj absolutny clen c: "
read C

D=$(evaluate "$B^2 - 4 * $A * $C")
X1=$(evaluate 3 "(-1 * $B + sqrt($D) ) / (2*$A)")
X2=$(evaluate 3 "(-1 * $B - sqrt($D) ) / (2*$A)")

echo "Diskriminant kvadratickej rovnice je $D"
echo "Korene kvadratickej rovnice su $X1 a $X2"

Záver

Cieľom článku bolo priblížiť jazyk bc, pomocou ktorého môžete rozšíriť svoje bash skripty. Toto však nie je jediné využitie, ktoré jazyk bc ponúka. Je v 8;om totiž možné písať vlastné funkcie, riadiť beh programu podmieneným vykonávaním, používať cykly a rovnako obsahuje funkcie na prácu so vstupom a výstupom. O tomto využití jazyka bc si však povieme niekedy nabudúce.

Použitá literatúra

    • RE: Matematika v príkazovom riadku 19.06.2013 | 09:17
      Avatar jimik   Návštevník
      parada, ale 255 in hexa is FF ;-)
      • RE: Matematika v príkazovom riadku 19.06.2013 | 11:36
        Avatar mirek biňas Fedora 35  Administrátor
        Fakt - mas pravdu - chyba je v tomto riadku:
        $ echo "16 in hexa is $RESULT"
        Spravne malo byt
        $ echo "255 in hexa is $RESULT"
        • RE: Matematika v príkazovom riadku 19.06.2013 | 14:18
          Avatar bedňa LegacyIce-antiX  Administrátor
          Mirek, máš úplne vpravo hore nad článkom tlačítko "Editovať článok"? Teda ak nie opravím to ja.
          Táto správa neobsahuje vírus, pretože nepoužívam MS Windows. http://kernelultras.org
          • RE: Matematika v príkazovom riadku 19.06.2013 | 14:19
            Avatar mirek biňas Fedora 35  Administrátor
            nie - zatial nemam. myslim, ze zatial editovat vedia len admini.
            • RE: Matematika v príkazovom riadku 19.06.2013 | 21:57
              Avatar bedňa LegacyIce-antiX  Administrátor
              Opravené.
              Táto správa neobsahuje vírus, pretože nepoužívam MS Windows. http://kernelultras.org
              • RE: Matematika v príkazovom riadku 20.06.2013 | 07:56
                Avatar mirek biňas Fedora 35  Administrátor
                vdaka