12.2. Příkaz SEARCH Příkaz SEARCH slouží k prohledání indexovaného pole (tabulky) za účelem vyhledání prvku vyhovujícího zadané podmínce a k naplnění zadaného speciálního indexu tak, aby se programátor mohl prostřednictvím tohoto speciálního indexu na nalezený prvek odvolat. Formát 1: - - - - | | spec.index | | SEARCH položka | VARYING < UI-položka > | [AT END příkaz...] | | cel.položka| | - - - - - - - - | | příkaz ... | | < WHEN podmíněný-výraz | | > ... | |NEXT SENTENCE| | - - - - Formát 2: SEARCH ALL položka [AT END příkaz ...] - - | příkaz ... | WHEN podmíněný-výraz | | |NEXT SENTENCE| - - Pravidla společná pro oba formáty: 1) Položka uvedená za slovem SEARCH resp. SEARCH ALL musí ve svém popisu obsahovat klauzuli OCCURS s podklauzulí INDEXED. Přesto se však při zápisu této položky za SEARCH (ALL) nemají uvádět indexy, a to proto, že tato položka zde označuje celé pole (tabulku) a nikoliv jen jeden její prvek. (Uvedení indexů je kvalifikováno jako mírná syntaktická chyba, avšak uvedené indexy jsou v MX COBOLu přijímány, přičemž pomocí nich může uživatel výrazně zrychlit provádění příkazu SEARCH pro vícerozměrnou tabulku, uvede-li zde první až předposlední index používaný v podmíněném výrazu v klauzuli WHEN; podrobněji viz poznámku u chyby 5405 v přehledu syntaktických chyb). Kvalifikace smí být uvedena. Označme délku této položky (tj. jednoho prvku pole) písmenem d a počet opakování této položky (jenž smí být i proměnný) písmenem n. 2) V rámci téže věty programu se další příkaz SEARCH nesmí vyskytnout mezi příkazy uvedenými za AT END, smí se však vyskytnout mezi příkazy uvedenými za "WHEN podmíněný-výraz". Tím je však předcházející příkaz SEARCH ukončen a všechny následující klauzule WHEN v téže větě programu již budou patřit k tomuto novému příkazu SEARCH. 3) Dojde-li k provádění příkazů uvedených za AT END nebo za nikoliv poslední klauzulí "WHEN podmíněný-výraz" a není-li mezi těmito příkazy příkaz způsobující odskok nebo ukončení výpočtu, provede se při dosažení nejbližšího slova WHEN odskok na první příkaz za tečkou (přesněji: odskok na konec rozsahu příkazu SEARCH, viz 5.1.F). (V cílovém programu se před každým WHEN vygeneruje ještě instrukce skoku na první příkaz za tečkou resp. na konec rozsahu příkazu SEARCH.) 4) Příkazy uvedené za AT END se provádějí tehdy, nebyl-li při žádné zkoumané hodnotě speciálního indexu shledán podmíněný výraz uvedený za slovem WHEN (resp. při formátu 1: žádný z podmíněných výrazů uvedených za slovy WHEN) pravdivým (viz dále). Při neuvedení klauzule AT END se v této situaci provede hned odskok na první příkaz za tečkou resp. na konec rozsahu příkazu SEARCH (stejně jako kdyby byla uvedena klauzule AT END s nějakým neúčinným příkazem, např. "AT END THEN WHEN ..."). 5) Je-li mezi příkazem SEARCH (tj. jeho klauzulí AT END resp. místem, na němž by mohla být uvedena) a jeho první klauzulí WHEN anebo mezi dvěma klauzulemi WHEN patřícími k témuž příkazu SEARCH uveden příkaz IF resp. ON nebo příkaz PERFORM bez procedury, musí v témže úseku věty programu (tj. ještě před nejbližší následující klauzulí WHEN) ležet i případná klauzule ELSE, END-IF resp. END-PERFORM patřící k tomuto příkazu IF, ON resp. PERFORM. Je-li zde uveden příkaz PERFORM bez procedury, jenž klauzuli END-PERFORM vůbec nemá, předpokládá se implicitní klauzule END-PERFORM bezprostředně před nejbližší následující klauzulí WHEN (pokud ovšem nebude rozsah příkazu PERFORM ukončen ještě dříve vzhledem k obdobnému pravidlu platícímu pro klauzule ELSE a END-IF; viz 5.1.F). Za poslední klauzulí WHEN ve větě programu smí být příkazy IF, ON a PERFORM uvedeny bez jakéhokoliv omezení. 6) Je-li příkaz SEARCH uveden v téže větě programu mezi příkazem IF/ON a jeho klauzulí ELSE nebo mezi příkazem IF/ON bez klauzule ELSE a jeho klauzulí END-IF nebo mezi klauzulí ELSE a klauzulí END-IF patřící k témuž příkazu IF/ON anebo mezi příkazem PERFORM bez procedury a jeho klauzulí END-PERFORM, musí v témže úseku věty programu (tj. ještě před touto klauzulí ELSE/END-IF/END-PERFORM) ležet i všechny klauzule WHEN patřící k tomuto příkazu SEARCH (viz 5.1.F). Za příkazem IF/ON bez klauzulí ELSE i END-IF nebo za klauzulí ELSE, jejíž příkaz IF/ON nemá klauzuli END-IF, nebo za příkazem PERFORM smí být příkaz SEARCH uveden bez jakéhokoliv omezení. 7) Zápis NEXT SENTENCE má význam prázdného příkazu a neprovádí žádnou akci, přičemž tento zápis může být vždy vynechán. Bývá obvyklé (i když to není povinné), aby za slovy NEXT SENTENCE (resp. pouze NEXT) následovala tečka nebo některá z klauzulí WHEN, ELSE, END-IF nebo END-PERFORM. Funkce pro formát 1: Příkaz SEARCH formátu 1 (tzv. "sekvenční SEARCH") se používá k prohledávání tabulky, která nemusí být nijak setříděná podle nějakých klíčů. V příkazu SEARCH hraje zvláštní roli jeden speciální index (budeme mu říkat "řídicí speciální index"), který je určen těmito pravidly: 1) Je-li v příkazu SEARCH uvedena klauzule VARYING se speciálním indexem patřícím k položce uvedené za slovem SEARCH (tj. definovaným v jejím popisu v klauzuli OCCURS INDEXED), bude řídicím speciálním indexem tento speciální index. 2) Ve všech ostatních případech bude řídicím speciálním indexem ten speciální index, který je v klauzuli INDEXED v popisu položky uvedené za slovem SEARCH uveden jako první. Před začátkem provádění příkazu SEARCH musí prográmátor provést následující akce: 1) Musí nastavit počáteční hodnotu řídicího speciálního indexu. Při obvyklém způsobu práce určuje tato hodnota ten prvek tabulky, od něhož počínaje se má tabulka začít sekvenčně prohledávat; tato hodnota by tedy měla být nezáporným násobkem čísla d, avšak měla by být menší než n * d. (Např. chceme-li začít prohledávat tabulku od k-tého prvku, provedeme před příkazem SEARCH nejprve příkaz "SET I TO k"). 2) Je-li uvedena klauzule VARYING, musí dát do údaje uvedeného za VARYING vhodnou počáteční hodnotu dle účelu, k němuž tento údaj používá (viz dále). Činnost příkazu SEARCH pak probíhá takto: 1) Testuje se, zda hodnota řídicího speciálního indexu je nejvýše rovna číslu (n-1)* d, tj. zda pořadí odpovídající této hodnotě je nejvýše rovno číslu n, neboli zda řídicí speciální index ještě ukazuje dovnitř tabulky. 2) Je-li tato hodnota nejvýše rovna číslu (n-1)*d, vyhodnocují se postupně všechny podmíněné výrazy uvedené v jednotlivých klauzulích WHEN. Je-li některý z nich pravdivý, další vyhodnocování se již neprovádí, a se zachovanou hodnotou řídicího speciálního indexu se provádějí příkazy uvedené za tímto podmíněným výrazem, čímž je činnost příkazu SEARCH ukončena. Jsou-li při této hodnotě řídicího speciálního indexu všechny podmíněné výrazy uvedeném v jednotlivých klauzulích WHEN nepravdivé, bude řídicí speciální index zvětšen o hodnotu d (tzn. pořadí se zvětší o 1, čímž se přechází na následující prvek tabulky). Je-li uvedena klauzule VARYING s jiným údajem než s řídicím speciálním indexem, bude navíc zvětšena i hodnota údaje uvedeného za VARYING, a to takto: a) U speciálního indexu (jenž patří k jiné tabulce) o délku té položky, v jejímž popisu je definován. b) U položky s USAGE INDEX o celé číslo d. c) U celočíselné numerické položky o 1. Pak se přichází znovu na bod 1). 3) Je-li hodnota řídicího speciálního indexu větší než (n-1)*d, znamená to, že při všech hodnotách řídicího speciálního indexu od počáteční hodnoty až do konce tabulky byly všechny podmíněné výrazy uvedené ve všech klauzulích WHEN nepravdivé, takže hledaný prvek tabulky nebyl nalezen. V tom případě se provádějí příkazy uvedené za AT END (resp. při neuvedení klauzule AT END se odskakuje přímo na první příkaz za tečkou), čímž je činnost příkazu SEARCH ukončena. Poznámky: 1) Na podmíněné výrazy uvedené za slovy WHEN nejsou kladena žádná omezení, lze použít obecný formát (viz 5.4.). Rozumný smysl mají ovšem pouze takové podmíněné výrazy, jejichž pravdivost závisí na hodnotě řídicího speciálního indexu anebo na hodnotě údaje uvedeného v klauzuli VARYING. 2) Použití klauzule VARYING má význam tehdy, když se v podmíněných výrazech uvedených za slovy WHEN mimo prvků tabulky uvedené za slovem SEARCH objevují i prvky jiných tabulek (většinou jedné, někdy však i více), jejichž indexy mají být zvětšovány stejně jako řídicí speciální index; např. chceme-li srovnávat stejnolehlé prvky dvou nebo více tabulek. Kdyby tyto jiné tabulek měly touž délku opakujícího se úseku paměti jako tabulka uvedená za slovem SEARCH, bylo by možné pro jejich indexování použít řídicí speciální index. Tabulky s jinou délkou opakujícího se úseku paměti však musí být indexovány buďto vlastním speciálním indexem nebo (jak je nutné v případě více těchto jiných tabulek) celočíselnou numerickou položkou. Tento index se pak uvede v klauzuli VARYING, načež příkaz SEARCH při zvětšování řídicího speciálního indexu bude automaticky zvětšovat i tento index, čímž přejde na další prvek nejen v prohledávané tabulce uvedené za slovem SEARCH, nýbrž i v ostatních současně zpracovávaných tabulkách. Schéma činnosti příkazu SEARCH formátu 1 při použití dvou klauzulí WHEN: I označuje hodnotu řídicího speciálního indexu. | | -------------- ------------------ | | ne |příkazy uvedené | ------>|I <= (n-1)*d|-------->|za AT END, je-li|--->- | | ? | |AT END použito | | | -------------- ------------------ | | |ano | | -------------- ------------------ | | |podm.výraz |pravdivý |příkazy uvedené | | | |za prvním |-------->|za prvním WHEN |--->| | | WHEN | ------------------ | | -------------- | | |nepravdivý | | -------------- ------------------ | | |podm.výraz |pravdivý |příkazy uvedené | | | |za druhým |-------->|za druhým WHEN |--->| | | WHEN | ------------------ | | -------------- | | |nepravdivý | | -------------- ------------ | | I + d -> I | | příkazy| | -------------- |za tečkou | | -------------------------- ------------ | |zvětšení hodnoty údaje | | |uvedeného za VARYING | | |(je-li klauzule VARYING | | |uvedena s jiným údajem | | |než s řídicím speciálním| | |indexem) | | -------------------------- -<------------ Příklad: V paměti je dřívějším výpočtem vytvořena tabulka o N prvcích, z nichž každý má čtyřbytové identifikační číslo a 56 bytů dalších údajů. Tato tabulka není nijak setříděná. Ze SYSIPT čteme věty, jejichž první čtyři byty obsahují identifikační čísla položek z tabulky a jejichž zbývající byty jsou bezvýznamné. Pro každé takto zadané identifikační číslo máme vytisknout odpovídající prvek tabulky. 77 STITEK PIC X(4). 01 TABULKA. 02 PRVEK OCCURS 1000 DEPENDING N INDEXED I. 03 CISLO PIC X(4). 03 DALSI-UDAJE PIC X(56). : CTENI. ACCEPT STITEK AT END STOP RUN. SET I TO 1. SEARCH PRVEK AT END DISPLAY 'NENALEZENY PRVEK' STITEK WHEN CISLO (I) = STITEK DISPLAY PRVEK (I). GO TO CTENI. Funkce pro formát 2: Příkaz SEARCH formátu 2 (se slovem ALL) se používá k prohledávání tabulky, která je setříděná podle jednoho nebo několika klíčů. Bylo by možné i zde použít příkaz SEARCH formátu 1, avšak u rozsáhlých tabulek by bylo sekvenční prohledávání příliš pomalé. Příkaz SEARCH ALL umožňuje rychlejší prohledávání tabulky metodou půlení intervalu, může však být použit pouze tehdy, jsou-li splněny tyto podmínky: 1) Položka uvedená za SEARCH ALL musí mít ve svém popisu v klauzuli OCCURS mimo podklauzule INDEXED ještě i podklauzuli ASCENDING/DESCENDING. Položky v tabulce musí být setříděné dle klíčů uvedených v klauzuli ASCENDING/DESCENDING (vyžaduje se ovšem setřídění pouze podle těch klíčů, které jsou použity v podmíněném výrazu uvedeném za slovem WHEN), jak je popsáno v odstavci 4.5.9.2. 2) Podmíněný výraz za slovem WHEN musí být buďto jediný jednoduchý podmíněný výraz nebo několik jednoduchých podmíněných výrazů spojených slovy AND. Jsou povoleny pouze následující jednoduché podmíněné výrazy: a) Relační test zjednodušeného formátu: - - - - | položka | - - | položka | | literál | | = | | literál | < fig.konstanta > IS < > < fig.konstanta > | ALL-klauzule | | EQUAL TO | | ALL-klauzule | - - - - - - Na levé nebo na pravé straně relačního testu (nikoliv však na obou současně) musí být uveden některý z klíčů, tj. některá z položek uvedených v klauzuli ASCENDING/DESCENDING v popisu položky uvedené za SEARCH ALL. b) Test podmínkového jména, jehož podmínková proměnná musí být některý z klíčů tj. některá z položek uvedených v klauzuli ASCENDING/DESCENDING v popisu položky uvedené za SEARCH ALL; přitom v klauzuli VALUE v popisu tohoto podmínkového jména musí být uveden pouze jeden údaj (takže se prakticky jedná o případ a). V podmíněném výrazu smí být testována každá položka z příslušné klauzule ASCENDING/DESCENDING (tj. klíč) nejvýše jednou. Výběr klíčů testovaných v podmíněném výrazu smí být zcela libovolný (MX COBOL nevyžaduje, aby s každým testovaným klíčem byly povinně testovány i všechny klíče vyšší priority, tj. uvedené v klauzuli ASCENDING/DESCENDING před ním). Pořadí jednoduchých podmíněných výrazů v podmíněném výrazu uvedeném za slovem WHEN nemusí být shodné s pořadím klíčů v klauzuli ASCENDING/DESCENDING. 3) Řídicím speciálním indexem je vždy první speciální index uvedený v klauzuli INDEXED v popisu položky uvedené za SEARCH ALL. Počáteční hodnota tohoto speciálního indexu je bezvýznamná, programátor ji nemusí nastavovat. Příkaz SEARCH ALL hledá (metodou půlení intervalu) takovou hodnotu řídicího speciálního indexu, při níž je podmíněný výraz uvedený za WHEN pravdivý. Pokud ji najde, budou se provádět příkazy uvedené za tímto podmíněným výrazem. Pokud ji nenajde, znamená to, že taková hodnota mezi hodnotami 0, d, 2d, ..., (n-1)*d odpovídajícími pořadím 1,2,3,...,n neexistuje, takže v tabulce neexistuje prvek vyhovující zadané podmínce. V tom případě se provádějí příkazy uvedené za AT END (resp. při neuvedení klauzule AT END se odskakuje přímo na první příkaz za tečkou), přičemž hodnota řídicího speciálního indexu není definována. V obou případech je tím činnost příkazu SEARCH ukončena. Poznámka: V obou formátech příkazu SEARCH je přípustné, aby položka uvedená za SEARCH (ALL) byla podřízena nebo nadřízena položce s klauzulí OCCURS. Pak vlastně příkazem SEARCH prohledáváme vícerozměrnou tabulku. K tomu je zapotřebí provést tolik příkazů SEARCH, kolik dimensí (tj. klauzulí OCCURS) má tabulka. Prvním příkazem SEARCH určíme první speciální index, druhým příkazem SEARCH určíme druhý speciální index (přičemž první speciální index je již používán jako konstantní) atd. Podle situace lze kterýkoliv z těchto příkazů SEARCH nahradit příkazem PERFORM s klauzulí VARYING nebo jej rozepsat pomocí příkazů IF, GO, ADD, MOVE, SET atd. Příklad: Podnik má 30 závodů, každý závod má nejvýše 1000 zaměstnanců (nepřípustný případ, kdy závod má více než 1000 zaměstnanců, se projeví obsazením položky ZAMESTNANEC s indexem 1001). 01 PODNIK. 02 ZAVOD OCCURS 30 ASCENDING JM-ZAV INDEXED I. 03 JM-ZAV PIC X(10). 03 ZAMESTNANEC OCCURS 1001 INDEXED J. 04 C-ZAM PIC 9(6). 04 UDAJE PIC X(24). Položky ZAVOD jsou setříděné podle JM-ZAV vzestupně, položky ZAMESTNANEC setříděné nejsou. Neobsazené položky ZAMESTNANEC jsou vždy až za všemi obsazenými položkami ZAMESTNANEC a mají C-ZAM nulové. Ze standardního vstupu čteme logické věty obsahující JM-ZAV a C-ZAM, přičemž vždy máme najít a vytisknout příslušnou položku ZAMESTNANEC. 01 STITEK. 02 S-JM-ZAV PIC X(10) 02 S-C-ZAM PIC 9(6). : Q1. ACCEPT STITEK AT END STOP RUN. SEARCH ALL ZAVOD AT END DISPLAY 'VADNE S-JM-ZAV ' STITEK WHEN JM-ZAV (I) = S-JM-ZAV SET J TO 1 SEARCH ZAMESTNANEC AT END GO TO PLNA-TAB WHEN C-ZAM (I J) = S-C-ZAM DISPLAY ZAMESTNANEC (I J) WHEN C-ZAM (I J) = ZERO DISPLAY 'VADNE S-C-ZAM ' STITEK GO TO Q1. PLNA-TAB. DISPLAY 'VICE NEZ 1000 ZAMESTNANCU V ZAVODE ' S-JM-ZAV. STOP RUN. Poznámka: Jak vyplývá z popisů a schémat, překladač využívá ze zadané tabulky (tj. z položky uvedené za slovem SEARCH resp. za slovy SEARCH ALL) pouze tyto informace: a) Délku opakující se položky d a počet opakování n. b) Který speciální index bude používán jako řídicí. c) Pouze u příkazu SEARCH ALL (tj. u formátu 2): Které z jednoduchých podmíněných výrazů uvedených za slovem WHEN se týkají setřídění vzestupného a které sestupného (aby bylo možno rozhodnout, zda se má řídicí speciální index zvětšit nebo zmenšit). Adresa tabulky ani její obsah se v příkazu SEARCH vůbec nevyužívají; při provádění příkazu SEARCH se vlastně pracuje pouze s řídicím speciálním indexem (a případně s údajem z klauzule VARYING) a nikoliv se zadanou tabulkou. Spojení příkazu SEARCH s určitou tabulkou je zprostředkováno pouze pomocí podmíněných výrazů uvedených za slovy WHEN, za jejichž logickou správnost a vhodnost plně odpovídá uživatel.