Hlavní navigace

Makro procesor GNU m4 (6)

29. 10. 2001
Doba čtení: 4 minuty

Sdílet

Makro procesor m4 obsahuje i makra, která dovolují vyhodnocovat aritmetické výrazy. V dnešním pokračování se je naučíme používat.
Aritmetika v m4

V m4 lze pomocí makra eval vyhodnocovat celočíselné výrazy:

eval(výraz [, číselná soustava [, šířka]])

Příkaz eval vyhodnotí matematický výraz a výsledek vrátí jako číslo o šířce šířka zapsané ve volitelně zadané číselné soustavě.

Výrazy musí být striktně celočíselné a mohou obsahovat následující operátory (seřazené sestupně podle priority):

Tabulka č. 206
 – unární minus
** umocnění
* / % násobení, celočíselné dělení, modulo (zbytek po dělení)
+ – sčítání, odčítání
<< >> bitový posun vlevo, bitový posun vpravo
== != > >= < <= porovnávání: rovná se, nerovná se, větší, větší nebo rovno, menší, menší nebo rovno
! logická negace (NOT)
~ bitová negace (NOT)
& bitový součin (AND)
^ bitový exkluzivní součet (XOR)
| bitový součet (OR)
&& logický součin (AND)
|| logický součet (OR)

Všechny operátory kromě umocnění (**) jsou asociativní zleva. Nevyhovuje-li vám pořadí vyhodnocování operátorů, můžete pochopitelně ke změně priority použít závorky.

Malá poznámka o kompatibilitě GNU m4 s ostatními verzemi: různé implementace makro procesoru používají ^ jako alternativní operátor pro umocnění. GNU m4 během svého vývoje u tohoto operátoru změnilo své chování: původně ^ znamenalo umocnění, ale nyní se tímto operátorem označuje bitový XOR.

Pár ukázek použití makra eval:

   eval(1 + -1)
=> 0
   eval(2 + 3 * 4)
=> 14
   eval((2 + 3) * 4)
=> 20
   eval(2 ** 8)
=> 256
   define(`obvod_ctverce', `eval(($1) * 4)')
=>
   obvod_ctverce(6)
=> 24

Pozor na práci s makry v aritmetickém výrazu:

   define(`konst', 10)
=>
   eval(`konst' / 4)
E> stdin:2: m4: Bad expression in eval: konst / 4
   eval(konst / 4)
=> 2

Můžeme skloubit naše znalosti o rekurzivním volání maker a aritmetické operátory obohatit o makro suma, které vypočte součet svých argumentů:

   define(`suma', `ifelse($#, 0, 0,
                          $#, 1, ``$1'',
                          `eval(suma(shift($@)) + `$1')')')
=>
   suma
=> 0
   suma(5)
=> 5
   suma(3,4,1,1,2)
=> 11
   Tady to drhne: suma(xyz)
=> Tady to drhne: xyz

Aby se makro suma dalo zodpovědně používat, bylo by třeba je obohatit o kontrolu, zda-li jsou všechny parametry opravdu čísly.

Relační operátory pro pravdivé vyhodnocení (true) vrací jedničku, pro nepravdu (false) nulu:

   eval(10 > 8)
=> 1
   eval((5 == 5) && (12 < 12))
=> 0

Jak již bylo řečeno, všechna čísla ve výrazech musí být celá. Procesor m4 rozpoznává několik zápisů číselných konstant podle jejich prefixů. Číslo bez prefixu je chápáno jako desítkové. Předřadíte-li číslu nulu („0“), bude m4 předpokládat, že se jedná o zápis v oktálním tvaru (osmičkové soustavě). Prefix „0ד označuje hexadecimální číslo (v šestnáctkové soustavě) a „0b“ číslo v binární (dvojkové) soustavě. V m4 ovšem nejste omezeni jen na tyto čtyři číselné soustavy. Chcete-li, můžete do výrazu makra eval zapisovat číselné konstanty v jakékoliv číselné soustavě se základem mezi 2 až 36 (včetně). Musíte jen použít prefix „0r“ následovaný ihned základem soustavy v decimálním tvaru, dvojtečkou („:“) a samotnými číslicemi, kterými je konstanta tvořena. U čísel v soustavách vyšších než desítkových se jako číslice používají i písmena a, b až z (nebo A, B až Z, což je totéž).

   eval(0b101)
=> 5
   eval(010)
=> 8
   eval(09)
E> stdin:3: m4: Bad expression in eval (excess input): 09
   eval(0xFF)
=> 255
   eval(0r36:z)
=> 35
   eval(0xF - 0b1111)
=> 0

Výsledek lze získat v libovolné číselné soustavě. Základ výstupní soustavy předejte makru eval v desítkovém tvaru (číslo z intervalu <2, 36>) jako druhý parametr a o víc se nemusíte starat:

   eval(255, 10)
=> 255
   eval(255, 16)
=> ff
   eval(35, 36)
=> z

Třetí argument makra eval určuje minimální šířku výstupu. Pokud je výsledek expanze menší, číselná hodnota se do nastavené minimální šířky doplní zleva nulami:

   eval(120, 10, 10)
=> 0000000120
   eval(-120, 10, 10)
=> -000000120
   eval(-120, 10, 2)
=> -120
   eval(255, 16, 5)
=> 000ff
   eval(-255, 16, 5)
=> -00ff

Pozor, abyste takto upravený výsledek omylem nepoužili znovu ve výrazu eval, pokud si přesně nejste jisti, co děláte:

   eval(eval(200, 10, 5))
=> 128
   eval(eval(200, 10, 2))
=> 200

Vnořený eval v prvním případě expandoval na 00200 a takovýto řetězec se stal vstupem pro druhé makro eval. 00200 je ovšem zápis čísla v oktálním tvaru, proto výsledek v desítkové soustavě bude 128, a ne 200, jak bychom asi čekali.

Zkratky pro +1/-1

Makro procesor m4 obsahuje navíc vestavěná makra incr a decr, která k číslu v parametru přičtou (resp. od něj odečtou) jedničku:

incr(číslo)
decr(číslo)

Příklady:

skoleni

   incr(3)
=> 4
   decr(7)
=> 6
   define(`konst', 10)
=>
   decr(konst)
=> 9

Tato makra vás sice ušetří psaní několika znaků, ale pořád to jaksi není ono. Pro potřeby inkrementování nebo dekrementování hodnoty uložené v nějakém makru zkuste tyto definice:

   define(`inkrementuj', `define(`$1', incr($1))')
=>
   define(`dekrementuj', `define(`$1', decr($1))')
=>
   define(`i', 10)
=>
   i
=> 10
   inkrementuj(`i')
=>
   i
=> 11

I zde je ovšem třeba ošetřit stavy, kdy inkrementované makro není definováno nebo neobsahuje číslo – pilný čtenář si jistě definice po nabytí všech potřebných znalostí doplní sám.

Autor článku

Michal Burda vystudoval informatiku a aplikovanou matematiku a nyní pracuje na Ostravské univerzitě jako odborný asistent. Zajímá se o data mining, Javu a Linux.