Hlavní navigace

Programujeme podmínky a smyčky v PostScriptu

28. 6. 2007
Doba čtení: 11 minut

Sdílet

V dnešní části seriálu o grafických formátech a metaformátech si řekneme, jakým způsobem je možné do programů vytvářených v PostScriptu vkládat podmínky a smyčky, které zajišťují opakovaný běh určité části programu. Uvidíme, že možnost tvorby programových smyček může vést k radikálnímu snížení velikosti celého souboru a také k tvorbě obrázků a diagramů, které by se ručně (či pomocí vektorového grafického editoru) vytvářely složitým a zdlouhavým způsobem.

Obsah

1. Řízení běhu programu – operátory „if“ a „ifelse“
2. Operátor „if“
3. Operátor „ifelse“
4. Programujeme smyčky v PostScriptu
5. Smyčka typu „repeat“
6. Smyčka typu „for“
7. Smyčka typu „loop“
8. Odkazy na další informační zdroje
9. Obsah dalšího pokračování tohoto seriálu

1. Řízení běhu programu – operátory „if“ a „ifelse“

Prakticky jakýkoli programovací jazyk, který má být reálně použitelný i pro tvorbu složitějších programů než jednoduchých maker či prostých několikařádkových seznamů příkazů, které se mají postupně provést, obsahuje některou z technologií určených pro řízení běhu programu. Tato technologie je typicky implementovaná ve formě zavedených a takřka univerzálně pojmenovaných strukturovaných příkazů typu „if-then-else“ (resp. pouze příkazu „if-then“ neobsahujícího větev „else“), popř. příkazů typu „switch-case“ (což je ve své podstatě pouze jiná podoba rozeskoků). Tyto příkazy jsou v různé syntaktické podobě obsaženy prakticky ve všech programovacích jazycích pocházejících z algolské větve vývoje, včetně Pascalu, Céčka a Javy.

U některých funkcionálních a objektově orientovaných programovacích jazyků se nejedná o příkazy zabudované přímo do jazyka, ale například o funkce s předem nevyhodnocovanými parametry (to je případ programovacího jazyka TCL), takzvané speciální formy (jazyky LISP a Scheme) či volání metod (Smalltalk). V PostScriptu jsou k řízení běhu programu určeny dva základní operátory: if a ifelse. Tyto operátory jsou sice přímo zabudované do jazyka, podobně jako ve výše zmíněných algolských jazycích, tj. jsou uloženy v neměnitelném (systémovém) slovníku (system dictionary), není však problém do uživatelského slovníku (user dictionary) přidat další operátory s podobnou funkcionalitou, například perlovský příkaz unless.

V případě, že by možnosti základních podmíněných operátorů nabízených PostScriptem (s jednou či oběma větvemi) nedostačovaly pro tvorbu složitějších podmínek, je možné vytvořit i operátory další; typicky se jedná o operátory typu „switch-case“ založené na slovnících (podrobnější informace o slovnících budou uvedeny v dalších částech tohoto seriálu). Nejprve si ve druhé kapitole popíšeme způsob zápisu a použití operátoru if (ovládající pouze jednu větev podmíněného příkazu), posléze se budeme ve třetí kapitole zabývat operátorem ifelse, který je určen pro ovládání obou větví podmíněného příkazu. PostScript neobsahuje žádnou obdobu podmíněného výrazu známého z céčka a Javy – je to prakticky zbytečné, protože rozdíl mezi výrazy a příkazy je v PostScriptu minimální (podmíněný příkaz může ponechat nějaké hodnoty na zásobníku operandů).

2. Operátor „if“

Operátor if se v programech zapisuje následujícím způsobem:

logický_výraz příkaz if 

Vidíme, že způsob zápisu tohoto příkazu odpovídá obrácené polské notaci (Reverse Polish Notation – RPN). Nejprve je zadán logický výraz, který po svém vyhodnocení uloží na zásobník svůj výsledek, tj. hodnotu true či false. Za logickým výrazem je zapsán příkaz či seznam příkazů umístěný mezi složené závorky. Tyto závorky zajistí, že interpreter nebude tyto příkazy volat přímo, ale uloží si je do zásobníku pro pozdější zavolání (na složené závorky se tedy můžeme dívat jako na obdobu LISPovské speciální formy „quote“ či příkazové závorky v TCL). Příkazy umístěné mezi složené závorky se nazývají procedury (s obvyklým významem) nebo také – především v originální dokumentaci – spustitelná pole (executable array).

Operátor if očekává, že před jeho spuštěním jsou na zásobníku operandů uloženy dvě hodnoty – logická hodnota (true/false) a odkaz na proceduru. Pokud je logická hodnota rovna true, spustí se procedura uložená na zásobníku a obě hodnoty jsou ze zásobníku odstraněny. V opačném případě, tj. když je logická hodnota rovna false, jsou pouze obě hodnoty odstraněny ze zásobníku bez provedení těla procedury, tj. příkazů většinou uvedených ve složených závorkách.

Příklad použití operátoru if:

x y gt {(x je vetsi nez y)} if 

Ve výše uvedeném příkladu se předpokládá, že x a y jsou proměnné typu celé či reálné číslo. Obě proměnné jsou nejprve vyhodnoceny, tj. jejich hodnoty jsou uloženy na zásobník (v PostScriptu je možné proměnné považovat za příkazy, které na zásobník uloží určitou hodnotu). Posléze je na tyto číselné hodnoty aplikován operátor gt, který na zásobník uloží pravdivostní (logickou) hodnotu true či false podle obsahu proměnných x a y. Obsah procedury specifikovaný ve složených závorkách je pouze uložen na zásobník (resp. může být v závislosti na implementaci na zásobník uložen pouze odkaz na tuto proceduru).

Následuje operátor if, který zjistí hodnotu výsledku porovnání a pokud byl výsledek vyhodnocen jako true, je procedura spuštěna. Vlastní tělo procedury obsahuje pouze řetězec, který je uložen na zásobník pro pozdější využití (například tisk na vytvářenou tiskovou stránku). Složitější příklad na použití operátoru if bude uveden v následujících kapitolách, ve kterých se budeme zabývat programovými smyčkami.

3. Operátor „ifelse“

Operátor ifelse je nejčastěji zapisován ve formě logický výraz, větev if (většinou reprezentovaná seznamem příkazů), větev else (opět reprezentovaná seznamem příkazů) a vlastní zápis operátoru ifelse. Syntaxe zápisu tohoto operátoru je tedy následující:

logický_výraz příkazy_větve_if příkazy_větve_else ifelse 

Příklad použití operátoru ifelse:

x y lt
    {(x je mensi nez y)}
    {(y je mensi nebo rovno x)}
ifelse 

U tohoto demonstračního příkladu opět předpokládáme, že x a y jsou proměnné typu celé či reálné číslo, protože tyto proměnné je možné porovnávat pomocí operátoru lt (less than). V závislosti na výsledku porovnání, kterým je podle aktuální hodnoty proměnných x a y logická hodnota true či false je provedena jedna z větví operátoru ifelse. Na zásobník operandů je uložen buď řetězec „x je mensi nez y“ nebo řetězec „y je mensi nebo rovno x“. Tento řetězec je možné použít například pro tisk na vytvářenou tiskovou stránku.

Operátory if a ifelse se v praxi používají buď při adaptaci tiskové stránky na různých výstupních zařízeních (například náhrada některých příkazů při tisku na osvitových jednotkách, podmíněný tisk čísel stran či hlaviček jednotlivých stránek, podmíněná práce s barvami, výběr obrázků s různým rozlišením podle možností tiskárny apod.), nebo pro řízený předčasný výskok z programových smyček, typicky nekonečné smyčky typu loop, která bude popsána v sedmé kapitole.

4. Programujeme smyčky v PostScriptu

Programovací jazyk PostScript obsahuje i několik operátorů, pomocí kterých je možné sestrojit programové smyčky, tj. opakující se části programového kódu. Z praktických důvodů se v programovacích jazycích vyskytují především dva typy smyček: počítané a nepočítané smyčky (popř. smyčky typu „for each“, které přebírají většinu dobrých vlastností smyček počítaných, přičemž tyto smyčky rozšiřují iterování přes pole, seznamy, n-tice či slovníky).

U smyček počítaných je pomocí jedné nebo více číselných konstant či proměnných určen počet opakování smyčky (a většinou se do pomocné proměnné v době běhu smyčky vkládá aktuální hodnota jejího počitadla), zatímco u smyček nepočítaných se používá obecnější podmínka pro ukončení smyčky, která se explicitně vyhodnocuje až v době běhu programu. V následujících kapitolách si popíšeme počítanou smyčku typu „repeat“ (pátá kapitola) a „for“ (šestá kapitola) a následně nepočítanou nekonečnou smyčku typu „loop“ (sedmá kapitola).

5. Smyčka typu „repeat“

Nejjednodušším typem smyčky je v PostScriptu smyčka typu „repeat“. Pomocí této smyčky je možné zajistit běh části programu n-krát, přičemž n může být zadáno buď formou celočíselné konstanty nebo výrazu, který se vyhodnotí až v době běhu programu. Syntakticky vypadá zápis tohoto druhu smyčky následovně:

počet tělo_smyčky repeat 

Nejprve je vyhodnocen počet opakování. Následně je podle vyhodnocené hodnoty počtu opakovaně prováděno tělo smyčky. V případě, že interpreter PostScriptu spustí příkaz exit, provede se ihned ukončení běhu smyčky bez ohledu na aktuální stav počtu opakování. Většinou bývá příkaz exit (pokud je vůbec u tohoto typu smyčky použit) umístěn ve větvi operátoru if nebo ifelse. Po ukončení smyčky, ať už po provedení řádného počtu opakování či po násilném ukončení pomocí příkazu exit, interpreter pokračuje prvním příkazem, který nalezne za slovem repeat.

Jestliže je jako počet opakování nastavena hodnota 0, je zaručeno, že smyčka neproběhne ani jednou, interpreter dokonce ani nebude analyzovat obsah těla smyčky (v něm se však nesmí vyskytovat syntaktické chyby). Příklad smyčky, která neproběhne ani jednou, je velmi jednoduchý:

0 {(tato smyčka nikdy neproběhne)} repeat 

6. Smyčka typu „for“

Počítaná smyčka typu for v několika ohledech rozšiřuje možnosti výše popsané smyčky repeat. Zatímco se u smyčky typu repeat specifikoval pouze počet opakování a počitadlo smyčky nebylo v jejím těle dostupné, je u smyčky typu for možné zadat počáteční hodnotu počitadla (initial), koncovou hodnotu počitadla (limit, při překročení této hodnoty je smyčka automaticky ukončena) a hodnotu, o kterou se počitadlo zvýší při každém spuštění těla smyčky (increment). Navíc je před každým spuštěním těla smyčky uložena na zásobník operandů aktuální hodnota počitadla, kterou je možné ve smyčce využít například k přístupu ke složkám pole a dalším operacím.

V případě, že se hodnota počitadla neodstraní ze zásobníku, budou na něm postupně ukládány jednotlivé hodnoty číselné sekvence začínající počáteční hodnotou a končící zadanou koncovou hodnotou počitadla. Hodnota, o kterou se počitadlo smyčky zvětšuje, může být i záporná. V tomto případě je smyčka ukončena v tehdy, jestliže je hodnota počitadla nižší než zadaná koncová hodnota – z toho vyplývá, že by tato hodnota měla být nižší než hodnota počáteční. Jestliže je již před začátkem smyčky hodnota počitadla větší než koncová hodnota (resp. při záporném přírůstku menší než koncová hodnota), je zaručeno, že smyčka neproběhne ani jedenkrát.

Podobně jako u smyčky typu repeat, i zde je možné v těle smyčky kdykoli zavolat příkaz exit, po jehož spuštění interpreter PostScriptu pokračuje v provádění prvního příkazu, který nalezne za smyčkou. V každém případě se po ukončení smyčky ze zásobníku automaticky odstraní zadané hodnoty initial, incrementlimit (pokud by se místo příkazu exit použil příkaz stop, byla by situace složitější, proto se tento příkaz pro ukončení smyček většinou nepoužívá). Syntakticky vypadá zápis smyčky typu for následovně:

initial increment limit tělo_smyčky for 

Příklady použití smyčky typu for:

1 1 10 {} for     % na zásobník se uloží číselná sekvence 1 2 3 4 5 6 7 8 9 10
0 2 10 {} for     % na zásobník se uloží číselná sekvence 0 2 4 6 8 10
10 1 0 {} for     % tato smyčka neproběhne ani jedenkrát
0 -1 10 {} for    % ani tato smyčka nebude spuštěna
0 1 1 4 {add} for % provede se součet 0+1+2+3+4=10
3 -.5 1 { } for   % na zásobník se uloží číselná sekvence 3.0 2.5 2.0 1.5 1.0 

Následuje složitější příklad využívající počítanou smyčku typu for pro vykreslení jednoduchého obrázku složeného z několika úseček. V těle smyčky je využito hodnoty jejího počitadla pro výpočet koncových souřadnic vykreslovaných úseček. Každá úsečka tvoří samostatnou cestu, ale bylo by možné vytvořit i cestu jedinou, složenou z na sebe NEnavazujících částí. Podobným způsobem je možné vykreslit například i pravidelnou mřížku, logaritmickou mřížku, horizontální či vertikální pravítko apod. – to jsou operace, které se v prakticky všech vektorových editorech tvoří poměrně složitě a přitom mohou být v mnoha případech užitečné.

50 10 400 {    % inicializace smycky for
    newpath    % vytvoreni nove cesty
        % instrukce   zasobnik
        dup         % i i
        50          % i i 50
        moveto      % i        - moveto si vzala souradnici (i, 50)
        400         % i 400
        exch        % 400 i
        lineto      % --       - lineto si vzala souradnici (400, i)
    stroke     % rasterizace cesty
} for
showpage       % a vykresleni cele stranky 

451
Výsledek běhu předchozího programu

7. Smyčka typu „loop“

Nepočítaná smyčka typu loop je již svou podstatou smyčkou nekonečnou, tj. ukončení smyčky je nutné explicitně provést pomocí příkazu exit či stop. Příkazy exit a stop se od sebe odlišují především tím, že se po zavolání příkazu exit provede automatický úklid zásobníku tak, aby na něm byly uloženy pouze ty hodnoty, které se zde nacházely před inicializací a spuštěním smyčky, zatímco příkaz exit pouze smyčku ukončí a žádné manipulace se zásobníkem neprovádí. Vzhledem k tomu, že smyčka typu loop nemodifikuje obsah zásobníku operandů (tj. neukládá zde hodnotu počitadla ani nepotřebuje žádné inicializační hodnoty), mají v tomto případě příkazy exit a stop stejný význam (u dalších smyček to však neplatí, proto se u nich většinou volá pouze příkaz exit).

Tělo smyčky by tedy mělo obsahovat jeden z těchto „přerušovacích“ příkazů, typicky vložených do těla operátoru if nebo ifelse. Pokud není zavolán ani příkaz exit ani příkaz stop, je tělo smyčky prováděno stále dokola (tj. jedná se o klasickou nekonečnou smyčku) a interpretovaný postscriptový program je v tomto případě možné zastavit pouze externě vyvoláním příkazu interrupt, například posláním znaku Ctrl+C do tiskárny či pomocí přerušovacího tlačítka umístěného na jejím ovládacím panelu. Pokud je tělo smyčky zapsáno chybně, například tak, že se neustále zvyšuje počet položek uložených na zásobníku, může smyčka skončit také nějakou výjimkou, typicky typu stackoverflow. Syntakticky vypadá zápis smyčky typu loop velmi jednoduše:

tělo_smyčky loop 

Tento typ smyčky je použit například v následující signatuře. Všimněte si způsobu definice nových příkazů pomocí def (ten jsme si již vysvětlovali) a také způsobu ukončení smyčky pomocí operátoru if a exit:

/d{def}def/a{add}d/s{sub}d/u{usertime}d % Alun Jones, IBS, UW Aberystwyth
/x 0 d/y 0 d 9 9 scale 35 47 translate/b u d .1 setlinewidth{u b s 6e4 gt
{exit}if/x x 4 a d/v 1 x s d/x y 2 x mul 3 s abs sqrt x 0 lt{neg}if a d/y
v d x y moveto .1 0 rlineto stroke}loop showpage% You may need %!PS-Adobe 

452
Výsledek běhu předchozího programu

UX DAy - tip 2

8. Odkazy na další informační zdroje

  1. Adobe Systems: PostScript Language Reference Manual, The Red Book,
    Adobe Systems Incorporated, 2nd ed., Addison Wesley 1990.
  2. Adobe Type 1 Font Format (včetně ukázek s použitím smyček),
    Addison-Wesley
  3. PostScript Language Program Design,
    Addison-Wesley 1990, ISBN 0–201–14396–8
  4. PostScript Language Reference Manual,
    Addison-Wesley 1990, ISBN 0–201–18127–4
  5. PostScript Language Tutorial and Cookbook,
    Addison-Wesley 1990, ISBN 0–201–10179–3
  6. PostScript fractals,
    http://www.pvv­.ntnu.no/~ander­sr/fractal/Pos­tScript.html
  7. Fractal generation,
    http://www.chez­.com/emarsden/dow­nloads/
  8. Paul Bourke: PostScript Tutorial,
    http://local.was­p.uwa.edu.au/~pbou­rke/dataformat­s/postscript/
  9. A First Guide to PostScript,
    http://www.ta­ilrecursive.or­g/postscript/pos­tscript.html

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

V následující části tohoto seriálu si řekneme bližší informace o typech zásobníků, se kterými se můžeme při programování v PostScriptu setkat. Prozatím jsme si popisovali zejména operace prováděné se zásobníkem operandů, avšak uvidíme, že se zdaleka nejedná o jediný zásobník dostupný z postscriptového virtuálního stroje (VM).

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.