]) Konkrétní předmět. Ve větě je vyžadováno uvedení nějakého konkrétního předmětu a lingvistický modul zajišťuje, aby se tato věta dostala ke skriptu až ve chvíli, kdy bude rozhovorem upřesněn handle objektu. <Číslo pádu> určuje, ve kterém jmenném pádě bez předložky musí být argument uveden. Nepovinný parametr
může specifikovat povinnou předložku. Pokud není uveden, je předložka zakázána. semiobjekt(<číslo argumentu>, <číslo pádu> [,
]) Nekonkrétní předmět. Ve větě je vyžadován popis předmětu pomocí vlastností, předmět tedy není identifikován přesně. Do skriptu je popis předmětu předán v podobě memberu. Lingvistický modul vytvořený member nijak neprověřuje, není tedy např. vůbec zaručeno, že předmět uvedených vlastností ve světě existuje. Nepovinný parametr
může specifikovat povinnou předložku. Pokud není uveden, je předložka zakázána. ent(<číslo argumentu>, <číslo pádu> [,
]) Konkrétní ent. Skriptu je předán handle konkrétního enta. kde(<číslo argumentu>) Určení místa tvaru kde. Skriptu je předán handle; může se jednak jak o handle místnosti, tak o handle na konkrétní podložce (např. HO^police-5H) či v konkrétním kontejneru (např. HO skrin-8H). kam(<číslo argumentu>) Určení místa tvaru kam. Skriptu je předán handle; může se jednak jak o handle místnosti (HRkomoraH), tak o handle na konkrétní podložce (např. HO^police-5H) či v konkrétním kontejneru (např. HO skrin-8H).
5.10.2
Označení předmětů
Způsob zavedení označení nového předmětu v konfiguračním souboru vety nejlépe osvětlíme na příkladu: predmet(HOnaberackaH) ‘nazyvej‘ jmenem("sběračka").
5.10. SYNTAX SOUBORU VETY
55
predmet(HOnaberackaH) ‘nazyvej‘ jmenem("naběračka"). predmet(HOplotnaH, [podlozka]) ‘nazyvej‘ jmenem("plotna"). predmet(HOledniceH, [kontejner, podlozka]) ‘nazyvej‘ jmenem("lednice"). Uvedené řádky zavádějí slovní označení postupně pro předměty typu naběračka (typ identifikován handlem HOnaberackaH), plotna (HOplotnaH) a lednice (HOledniceH). V ukázce stojí za povšimnutí především možnosti konfigurace: • pro jeden typ předmětu je možné zavést více alternativních označení (řádka s HOnaberackaH) je v souboru dvakrát. • u předmětů je možné uvést seznam parametrů podlozka a kontejner, aby bylo o předmětu možno hovořit jako o podložce či kontejneru, případně obojí. • v predikátu jmenem(<"jméno">) je třeba uvést první pád podstatného jména, kterým má být daný typ předmětu identifikován. Další potřebné tvary jména odvozuje program automaticky, díky vestavěnému bohatému morfologickému slovníku. Pokud zvolíte pro typ předmětu jméno, které ve slovníku chybí, bude to během instanciace světa oznámeno jako chyba v souboru věty.1
5.10.3
Označení místností
Zavedení názvů místností opět osvětlíme na příkladu: mistnost("kuchyn", v) ‘nazyvej‘ jmenem("kuchyně"). mistnost("zahrada", na) ‘nazyvej‘ jmenem("zahrada"). mistnost("hudebni_salon", v) ‘nazyvej‘ souslovim([ jmeno("hudební"), jmeno("salón") ]). mistnost("pokoj_zahradnika", v) ‘nazyvej‘ souslovim([ jmeno("pokoj"), forma("zahradníka") ]). V rámci predikátu mistnosti(<"symbolický handle">,
56
KAPITOLA 5. BALÍK SVĚTA
úprav (forma(<...>)). Tím je zajištěno správné vyslovování např. věty: „Jdi do hudebního salónu.ÿ
Kapitola 6
Jazyk E 6.1
Úvodem
Následující text si klade za cíl být názornou příručkou pro všechny ty, kteří se chtějí blíže seznámit s jazykem E, vyvinutým v rámci projektu Enti jako prostředek pro řízení činností entů. Rysy jazyka jsou popsány v Teoretické dokumentaci, proto doporučujeme její přečtení jako formální úvod do jazyka.
6.2
Datové typy jazyka E
Jazyk E rozlišuje následující čtyři datové typy: handle – toto je atomický typ. Příkladem může být identifikátor konkrétního nebo obecného předmětu či vlastnosti, nebo číslo. seznam – seznam objektů typu handle. Příkladem může být seznam věcí, které jsou třeba pro vykonání některé činnosti, např. seznam neokopaných záhonů. member (superseznamu) – v podstatě řádek relační tabulky, neboli také specifikace daného předmětu vlastnostmi (viz Dogma, kapitola 3(str. 19) ) – vlastnosti předmětů). Používá se především při konkretizaci – tj. přiřazení konkrétního předmětu z množiny známých předmětů datové položce typu member, jejímž vlastnostem nejlépe odpovídá. Např. můžeme specifikovat špinavý prázdný hrnek, který se nachází v jídelně. superseznam – seznam objektů typu member. Vzhledem k existenci typů je nutné nějakým způsobem určit typy jednotlivých proměnných. Typ proměnné je v jazyce E určen z jejího identifikátoru podle systému uvedeného v tabulce 6.1(str. 58) . Zjednodušeně lze říci, že jména proměnných typu seznam mají tvar sJmenoSeznamu a jména proměnných typu superseznam mají tvar ssJmenoSuperseznamu. Jména proměnných typu member 57
58
KAPITOLA 6. JAZYK E
odpovídají tvaru mJmenoMemberu. Všechny ostatní proměnné jsou typu handle. Názvy všech proměnných musí začínat písmenem a mohou obsahovat pomlčky, podtržítka a číslice. regulární výraz ss[a-zA-Z 0-9]* m[a-zA-Z 0-9]* s[a-zA-Z 0-9]* [a-zA-Z][a-zA-Z 0-9]*
typ superseznam member seznam handle
příklad ssCisteKonve mCistaKonev sKonkretniKonve hKonkretniKonev
Tabulka 6.1: Přehled základních datových typů Objekty všech čtyř typů lze specifikovat proměnnými a výrazy. Upozornění Při práci s proměnnými je tedy nutné dbát ne jejich pojmenování, aby jim interpret přiřadil správný typ. Typy proměnných den, hodina, minuta, sekunda jsou po řadě handle, handle, member, seznam.
6.2.1
Typ handle
Typ handle je atomický typ, jehož prostřednictvím lze reprezentovat většinu základních struktur: čísla, identifikace předmětů a entů, vlastnosti a konstanty. Speciálním handlem je volná proměnná: . Volná proměnná se chová jako konstantní handle, který má význam „cokoliÿ. Konstanty odpovídají řetězcům uzavřeným do uvozovek. V názvu konstanty se nesmí vyskytovat znak tilda (˜). Příkladem může být například konstanta "hlad", jejíž hodnotou je obecné určení vlastnosti hlad enta. Konstanty lze používat buď standardní, nebo lze definovat vlastní. Standardní konstanty jsou uvedeny v souboru $ENTIROOT/data/ent/constant.$. Uživatelské konstanty lze definovat v balíku světa v souboru konstanty. Aby je bylo možné ve skriptech používat, je nutné svět nejprve instanciovat. Typ handle je umožňuje reprezentovat celá znaménková čísla. Jazyk E podporuje čísla v rozsahu -32766 až +32766, plus nekonečno se značí pINF, minus nekonečno nINF, číslo-nečíslo pak NAN. Čísla jsou vlastně jenom jednou z „vlastnostíÿ kódovaných v typu handle. Proměnná typu handle, která je vlastností, v sobě skrývá dvojí informaci: jednak kód vlastnosti a jednak její hodnotu. Rozsahy jednotlivých vlastností jsou popsány v Dogmatu (kapitola 3 (str. 19) ) a zejména v příloze C (str. 129) , nepřesahují však rozpětí 65535 prvků. Pokud je vlastnost použita jako argument numerické operace, je implicitně přetypována na číslo o odpovídající číselné hodnotě. Operace s typem handle Tabulka 6.2(str.
59)
nabízí přehled základních operací nad typem handle.
59
6.2. DATOVÉ TYPY JAZYKA E operace inkrementace proměnné x přičtení proměnné y k proměnné x dekrementace proměnné x odečtení proměnné y od proměnné x přiřazení proměnné y do proměnné x
zápis x++ x+=y x-x-=y x=y
Tabulka 6.2: Přehled operací s proměnnými typu handle Přetypování K vygenerování handlu vlastnosti o dané hodnotě lze použít výraz pro přetypování:
znak ^ * /, div %, mod + -
asociativita pravá levá levá levá levá levá levá
Tabulka 6.3: Přehled aritmetických operací
Porovnávání Proměnné typu handle lze porovnávat dvěma způsoby: jednak je možné testovat na identitu dvou handlů a jednak je možné porovnávat numerické hodnoty obsažené v těchto handlech (viz tabulka 6.4(str. 60) ). Příklad: Následující dvě porovnání jsou pravdivá. <"zalitost" 5> !== <"hlad" 5> <"zalitost" 5> == <"hlad" 5>
60
KAPITOLA 6. JAZYK E porovnávání identity rovnost nerovnost porovnávání numerických hodnot rovnost nerovnost menší rovno menší větší rovno větší
zápis === !== zápis == != <= < >= >
Tabulka 6.4: Porovnávání proměnných (Pokud ovšem hlad a zalitost nejsou definovány jako jedna vlastnost.)
6.2.2
Typ seznam
Proměnné typu seznam představují seznamy prvků typu handle. Výrazy typu seznam jsou ohraničeny hranatými závorkami [ a ] (viz tabulka 6.5(str. 60) ). prázdný seznam jednoprvkový seznam seznam prvků 1. .4, 6, 7 přiřazení do proměnné
[] [5],["konev"] [1+2+3+4+5+6+7-5] sZdrojeVody=["umyvadlo"+"drez"]
Tabulka 6.5: Práce se seznamy Ve výrazu typu seznam je možné použít operací + a - pro přidávání resp. pro odebírání prvků. Tyto operace se provádějí zleva doprava. Plus připojuje na konec. Mínus se chová jako množinový rozdíl s tím, že v seznamu může existovat více shodných prvků. Mínus odstraňuje prvky odleva. Příklad konkatenace a odčítání: výraz [1+2+3+4+4+3+2+1-4-3-3] je ekvivalentní výrazu [1+2+4+2+1]. Připojení do seznamu příkazy: sA+=5, sA+=sB je ekvivalentní příkazům: sA=[sA+5], sA=[sA+sB]. První varianta je však efektivnější, jelikož nedochází k vytváření kopie seznamu. Toto je nutné mít na paměti především při operacích s dlouhými seznamy. Odebírání ze seznamu je analogické přidávání. Pokud odebíráme ze seznamu neexistující prvek, seznam zůstane nezměněn. Výraz: [5-7] je tedy ekvivalentní výrazu [5]. Seznamy lze porovnávat pouze na rovnost nebo na nerovnost, přičemž při porovnávání se bere ohled na pořadí prvků v seznamech. Porovnávání pomocí operátorů <=, <, >=, > je sice syntakticky správné, ale výsledek je vždy nepravda. Délka seznamu je omezena 65535 prvky. Délku konkrétního seznamu lze zjistit predikátem c length(seznam,délka). První parametr je vstupní, druhý
6.2. DATOVÉ TYPY JAZYKA E
61
výstupní.
6.2.3
Typ member (superseznamu)
Proměnné typu member odpovídají jednomu řádku relační tabulky, která má následující sloupečky: "predmet" – handle předmětu "priorita" – určení priority "relevance" – určení relevance "mistnost" – handle místnosti "dlazdice" – handle dlaždice nebo vlastníka předmětu "kde" – bližší určení pozice předmětu: ”NA” nebo ”DO” "rozbity" – rozbitost předmětu "stari" – stáří předmětu "pocet" – počet předmětů "cistota" – čistota předmětu "cistitelny" – čistitelnost předmětu "ostrost" – ostrost předmětu "barva" – barva předmětu "jmeno" – jméno předmětu "skupina" – skupina, do níž předmět spadá (např. zelenina) "pristup" – handle přístupové dlaždice k předmětu "special" – vyhrazeno pro specifické účely "special1" – vyhrazeno pro specifické účely "special2" – vyhrazeno pro specifické účely "default member position" – vyhrazeno pro specifické účely Jednotlivá pole memberu jsou přístupná prostřednictvím svých názvů, tedy například: mA["pocet"]=5, mA["predmet"]="konev", X=mA["special"], mA["stari"]++. Prázdný member je reprezentovaný výrazem [:]. Proměnné typu member jsou nejčastěji využívány pro specifikování předmětů podle jejich vlastností. Čím více položek memberu je naplněno, tím konkrétnější je popis požadovaného předmětu. Pokud například member mA obsahuje v položce "predmet" hodnotu "konev" a v položce "barva" hodnotu "zelena", pak tento member obpovídá libovolné zelené konvi.
62
KAPITOLA 6. JAZYK E Upozornění Pokus o indexování memberu konstantou, která neodpovídá žádnému ze sloupečků tabulky není syntaktická chyba, ale je přeložen jako přístup na poslední sloupeček tabulky, tedy "default member position".
Kromě výše popsaných políček obsahuje member také seznam tzv. zakázaných handlů – seznam "ale ne". V tomto seznamu je možné uvést konkrétní handly předmětů, které (ačkoli mohou memberu odpovídat) si přejeme při případné konkretizaci na základě memberu ignorovat. Při přístupu k seznamu "ale ne" lze použít obdobných konstrukcí jako při práci s obyčejnými seznamy. Například příkaz: mUmyvadlo=["umyvadlo na zachode"+"umyvadlo v~kuchyni"] přiřadí do seznamu "ale ne" dvojici konkrétních umyvadel. Obdobně příkaz: sA=mUmyvadlo přiřadí do seznamu sA seznam "ale ne" memberu mUmyvadlo. Příkaz: sA=[mUmyvadlo+"umyvadlo na zahrade"] přiřadí do seznamu sA seznam "ale ne" memberu mUmyvadlo. V jazyce E lze používat i konstanty typu member. Jak taková konstanta vypadá, ukazuje následující příklad: ["predmet"="konev":"pocet"=2:"barva"!="zelena"] Pokud chceme předat seznam "ale ne" jako parametr volání funkce, musíme provést přetypování na seznam: funkceOcekavajiciSeznam(mUmyvadlo) // je špatně funkceOcekavajiciSeznam([mUmyvadlo]) // toto je dobře Upozornění Následující dva příkazy mají rozdílné následky: mA=mB – přepíše celé mA mA=[mB] – přepíše pouze seznam "ale ne" Porovnávání memberů se řídí obdobnými pravidly jako porovnávání obyčejných seznamů.
6.3. SKRIPTY JAZYKA E
6.2.4
63
Typ superseznam
Zatímco obyčejné seznamy obsahují položky typu handle, superseznamy obsahují položky typu member. Superseznamy mají mnohé společné s obyčejnými seznamy, jediným podstatným rozdílem vůči seznamu je zdvojení závorek v zápisu superseznamu. Výraz typu superseznam je tedy ohraničen symboly [[ a ]]. Prázdný superseznam je reprezentovaný výrazem [[]]. Jednoprvkovému superseznamu odpovídají např. výrazy: [[mX]] či [[["predmet"="konev"]]]. Výraz typu superseznam může být i složitější: [[mA+mB+mC-["predmet"="konev"]]]. Přiřazení do proměnné typu superseznam demonstruje následující příklad: ssZdrojeVodyVKuchyni=[[mKonev+mMotyka]] Zjišťování délky superseznamů a jejich porovnávání je obdobné jako u obyčejných seznamů.
6.3
Skripty jazyka E
Syntax skriptů je z velké části převzata z Prologu, skripty v jazyce E jsou však rozděleny podle svého určení na skripty výkonné a skripty výpočtové. Životní projevy strojového enta jsou vlastně interpretací jeho výkonných skriptů. Výkonné skripty předávají serveru prostředí ke zpracování jednotlivé atomické instrukce a zpracovávají jejich výsledky. Výpočtové, nebo také databázové skripty zpracovávají údaje v entově databázi a na jejich základě vyvozují další informace. Vzhledem k tomu, že „entíÿ svět je kolový, tak můžeme říci následující: 1. výpočtový skript běží vždy v nulovém čase (nula kol) 2. výpočtový skript nesmí volat výkonné skripty 3. výpočtový skript nesmí používat prostředky, k jejichž využití je nutný běh po více kol. Dobrým příkladem na výpočtový skript je skript, který spočítá, které umyvadlo je k entovi nejblíže. Výkonným skriptem pak může být například skript, který k tomuto umyvadlu dojde. Výkonné a výpočtové skripty se liší na první pohled tím, že ty výkonné mají tzv užitkovou funkci. Prolog umožnuje více variant predikátu (v jazyce E používáme termín skript), přičemž jednotlivé varianty se zkoušejí v pořadí, ve kterém jsou uloženy v prologovské databázi, resp. v pořadí, ve kterém jsou ve zdrojovém kódu. To platí i pro výpočtové skripty jazyka E. Nicméně, při výběru z více variant výkonného skriptu jsou všechny tyto varianty výkonného skriptu ohodnoceny na základě své užitkové funkce a poté jsou seřazeny podle získaných hodnot. (Při rovnosti hodnot užitkové funkce pro více variant je možné jejich
64
KAPITOLA 6. JAZYK E
spouštění v libovolném pořadí.) Užitkové funkce musí běžet v nulovém čase a platí pro ně stejná omezení jako pro výpočtové skripty. Užitková funkce může stejně jako jiné výpočtové skripty selhat – následkem toho není ta varianta, jejíž užitková funkce selhala, zařazena do seznamu variant a i kdyby všechny ostatní varianty skriptu selhaly, tato není spuštěna. Následující příklad uvádí výpočtový skript počítající Fibonaciova čísla: /* komentáře jsou jako v~C++, mohou být i vnořené */ // fib(h+,h-) fib(N,F):N==0, F=1. fib(N,F):N==1, F=1. fib(N,F):N>1, fib(N-1,F1), fib(N-2,F2), F=F1+F2. Zde si můžeme všimnout několik podstatných rozdílů oproti Prologu. V jazyce E není možné použít zápis fib(0,1)., který je obvyklý v Prologu. Jeho určitou obdobou je využívání faktů v databázi enta – blíže viz kapitola 6.3.10 (str. 73) . Další odlišnosti spočívají v práci s parametry skriptů. První parametr skriptu fib je vstupní a druhý výstupní – hodnota druhého parametru se nikde nezpracovává, takže například volání fib(10,-1) uspěje. Dojde k pokusu o nastavení hodnoty konstanty -1 na hodnotu desátého Fibonacciova čísla, toto však bude ignorováno. Pokud druhý parametr bude proměnná typu handle, tak její hodnota bude nastavena nezávisle na její předešlé hodnotě. Je možné napsat funkci fib tak, aby fungovala i jako test: fib_test(N,Fgiven):fib(N,Fcomputed), { Fgiven===_ ; Fgiven==Fcomputed }, Fgiven=Fcomputed. Na tomto příkladě můžeme vidět jednak testování proměnné Fgiven na rovnost volné proměnné a dále použití spojky OR. Složené závorky vyznačují složený příkaz. Pokud chceme spojit více podmínek spojkou OR a ne spojkou AND (značenou čárkou), musíme argumenty operace OR oddělit složenými závorkami od příkazů spojených spojkou AND. Toto je nutné i v následujícím případě: disjunkce:- {A;B;C}. disjunkce:- A;B;C.
// správně // špatně
6.3. SKRIPTY JAZYKA E
65
Spojky OR a AND můžeme libovolně vnořovat: vnorenaPodminka:A,{B;{A,B,Q};{F;{S,A}}},{C;D},E. Místo symbolů , a ; je možné použít výrazy AND a OR, takže následující zápis je ekvivalentní předešlému: vnorenaPodminka:A~AND {B OR{A AND B AND Q} OR {F OR {S~AND A}}} AND {C OR D} AND E. Z předchozích příkladů je zřejmé, jakým způsobem lze zkonstruovat skript bez parametrů. Následující příklad demonstruje špatně zapsaný skript bez parametrů a dále způsob, jakým lze vytvořit skript s prázdným tělem: disjunkce:- A,B,C. disjunkce():- A,B,C.
// správně // špatně
disjunkce:-. disjunkce.
// správně // špatně
Nyní nadešel vhodný okamžik pro první praktičtější příklad, na němž ozřejmíme použití užitkovývh funkcí. Vytvoříme výkonný skript pro najezení enta. Ent bude jíst perník z lednice, nebo mrkev ze záhonu. Má raději perník, ale není ochoten pro něj jít více jak dvojnásobnou vzdálenost než pro mrkev. Skript jako parametru očekává handly záhonu a ledničky. Užitkové funkce vrací záporná čísla, protože ent má raději bližší zdroj potravy. jez(hZahonMrkve,hLednicka)$cJsemKde(hKde), cVzdalenost(hKde,hZahonMrkve,Vzdalenost), return -2*Vzdalenost. :/* dojde na záhon a sní mrkev*/ . jez(hZahonMrkve,hLednicka)$cJsemKde(hKde), cVzdalenost(hKde,hLednicka,Vzdalenost), return -Vzdalenost. :/* dojde k lednici a sní koblihu*/ . Užitkové funkce jsou uvedeny před vlastním tělem výkonného skriptu a jsou uvozeny operátorem $-. Užitková funkce končí příkazem return hodnota. Častým jevem jsou výkonné skripty s jednou variantou. I u těchto skriptů je nutné napsat užitkovou funkci, například následující:
66
KAPITOLA 6. JAZYK E
rekni$return 0. :/* tady bude vlatní kód funkce rekni */ .
6.3.1
Předávání parametrů
V jazycích jako C/C++ a Pascal existují dvě možnosti předávání parametrů: hodnotou (”nevařené”) a odkazem (”vařené”). Jazyk E dovoluje předávání parametrů oběma způsoby. Syntax je následující: funkce(nevarenyParametr, ~varenyParametr):/* tělo funkce */ . Při volání funkce v C/C++ a Pascalu dochází k alokování struktury na zásobníku, která obsahuje mimo jiné lokální proměnné dané funkce. V jazyce E existuje obdobný mechanismus: Při spuštění skriptu také dojde k alokování struktury a jejím přidání na zásobník. Při předávání parametru hodnotou dojde k vyhrazení místa ve struktuře příslušející volanému skriptu a překopírování obsahu datové položky volajícího skriptu do skriptu volaného. Při předávání parametru odkazem volaný skript přistupuje do paměťové struktury volajícího skriptu (případně jiného předchůdce, pokud je řetězec předávání odkazem delší). Parametr předávaný hodnotou se v případě úspěchu skriptu kopíruje do volajícího skriptu, v případě neúspěchu se změny v hodnotách parametrů ignorují. Mějme dvě funkce, které se liší pouze ve způsobu předávání parametrů: funkceA(X):X=5, fail. funkceB(~X):X=5, fail. Tyto funkce zavoláme v následujícím kódu: print("promenna X ma hodnotu ", X), try funkceA(X), print("promenna X ma hodnotu ", X), try funkceB(X), print("promenna X ma hodnotu ", X),
//promenna X ma hodnotu _ //promenna X ma hodnotu _ //promenna X ma hodnotu 5
Obě funkce selžou, ale změny vykonané na parametru předávaném odkazem se „nezapomenouÿ. Příkaz fail představuje jako v Prologu vždy nesplněný cíl, funkce print je funkce provádějící výpis svých parametrů na standardní výstup. Příznak try má význam ”zkus to a pokud to nevyšlo, tak stejně pokračuj dál”.
6.3. SKRIPTY JAZYKA E
6.3.2
67
Typ backtrackingu
Skript je tvořen posloupností příkazů. Tyto příkazy mohou selhat či uspět, což má vliv na běh skriptu. V jazyce E si pro každý skript můžeme zvolit jednu ze dvou možných variant interpretace. První možností je interpretace bez backtrackingu, tj. příkazy jsou prováděny od začátku skriptu a pokud některý selže, selže celý skript (nebo přesněji pouze vykonávaná varianta). Druhým způsobem interpretace je tzv. lineární backtraking. Lineární backtracking je velmi jednoduchý, při běhu skriptu si interpret pamatuje, která varianta předchozích příkazů uspěla, a pokud nějaký příkaz selže, tak přejde na příkaz předchozí a požaduje se jeho další varianta. Příkazy typu porovnávání a dosazení mají jednu variantu, takže při požadavku na další variantu selžou a interpret přejde na příkaz před nimi. Pokud při backtrackingu selže i první příkaz skriptu, tak selže celý skript. Těla skriptů, v nichž se nemá backtrackovat, jsou uvozena operátorem :-; těla skriptů, v nichž má být backtracking povolen, jsou uvozena operátorem :--. Backtracking lze, stejně jako v Prologu, ovlivňovat i prostřednictvím operátoru řezu !. Následující příklad demonstruje použití backtrackingu pro filtrování superseznamu podle relevance jednotlivých memberů: // vyberRelevantni(ss+,h+,ss-) vyberRelevantni(ssIn,hMinRelevance,~ssOut):-ssOut=[[]], c_length(ssIn,I), repeat, if I<=0 then COMMIT fi, Member(mX,ssIn), I--, mX["relevance"]>=hMinRelevance, ssOut+=mX, fail. Skript používá několik vestavěných příkazů jazyka E. Funkce c length jako svůj první parametr očekává (super)seznam a v druhém argumentu vrací jeho délku. Predikát repeat stejně jako v Prologu představuje cíl, který je vždy splněn. Ve spojení s příkazem fail jej lze využít ke konstrukci jednoduchých repeat-cyklů (viz kapitola 6.3.5(str. 70) ). Důležitou a často využívanou funkcí je Member, která v prvním parametru postupně generuje prvky (super)seznamu uvedeného jako druhý parametr. Funkce Member je generující, existuje ještě testující funkce isMember, která uspěje tolikrát, kolikrát je první argument prvkem druhého. Skript mimo jiné demonstruje použití podmínky if-then(-else)-fi. Podrobně se podmínkami zaobírá kapitola 6.3.3(str. 69) . Poslední dosud neprobraným příkazem z výše uvedeného skriptu je COMMIT. COMMIT způsobí okamžité úspěšné ukončení skriptu, tj. předá se řízení předkovi.
68
KAPITOLA 6. JAZYK E
Podrobně je příkaz probrán v kapitole 6.3.23(str.
88) .
Následuje příklad funkce počítající počet výskytů handlu v seznamu: vyskyty(sIn,hHledane,~hPocet):-hPocet=0, isMember(hHledane,sIn), hPocet++, fail. vyskyty(sIn,hHledane,~hPocet):-true. // true je splněný cíl, nemá žádný efekt, // možno použít na zpřehlednění kódu Skripty vyskyty a vyberRelevantní jsou silně závislé na typu backtrackingu. Pokud bychom u nich zaměnili :-- za :-, přestaly by tyto skripty fungovat. Skript fib na počítání Fibonacciho čísel však bude fungovat shodně. Lineární backtracking má oproti backtrackingu v Prologu jeden podstatný rozdíl: při přesunu na předchozí příkaz (klauzuli) se neobnovují hodnoty proměnných. Jaké problémy to může působit a jak je obejít ukáže následující příklad, který vypíše Fibonacciho čísla za pomocí skriptů fib a fib test: vypisFibonaccihoCisla(Od,Do):-if Od>Do then print("nic k~vypisu"), COMMIT fi, if Od<0 then print("zaporna Fibonacciho cisla nejsou definovana"), COMMIT fi, N=Od, repeat, fib(N,F), print("Fibonacciho cislo ", N, " je ", F), N++, N>Do. vypisFibonaccihoCisla(Od,Do):-if Od>Do then print("nic k~vypisu"), COMMIT fi, if Od<0 then print("zaporna Fibonacciho cisla nejsou definovana"), COMMIT fi, N=Od, repeat, fib_test(N,F), print("Fibonacciho cislo ", N, " je ", F), N++, N>Do.
6.3. SKRIPTY JAZYKA E
69
Druhý skript nebude fungovat správně – nevypíše více jak dvě Fibonacciho čísla (a to pouze v případě, že má vypisovat od nuly, jinak nejvýše jedno), protože na začátku je F volná, ale po prvním volání fib či fib test dojde k jejímu naplnění číslem. Skriptu fib to nevadí, ale skriptu fib test ano; dokonce dojde k zacyklení. Po malé úpravě již však vše bude v pořádku: vypisFibonaccihoCisla(Od,Do):-if Od>Do then print("nic k~vypisu"), COMMIT fi, if Od<0 then print("zaporna Fibonacciho cisla nejsou definovana"), COMMIT fi, N=Od, repeat, fib_test(N,F), print("Fibonacciho cislo ", N, " je ", F), N++, F=_, N>Do.
6.3.3
Podmínky
Běh skriptů lze řídit prostřednictvím podmínek. Jazyk E k tomuto účelu disponuje konstrukcí if-then(-else), která je syntakticky totožná s podmínkami v Bourne shellu: if podmínka then příkazthen1, ..., příkazthenN else příkazelse1, ..., příkazelseN fi Větev else je nepovinná a lze ji vynechat. Stejně jako v Prologu je i v jazyce E konstrukce if-then-else vyjádřitelná standardními prostředky jazyka. Celý blok if-then-else je interpretem interně přeložen na volání následujího generovaného kódu: __scr123(všechny proměnné obsažené v~bloku):podmínka, !, // řez jako v~Prologu, backtracking nepustí vpřed // a nepustí interpret do další varianty příkazythen.
70
KAPITOLA 6. JAZYK E
__scr123(všechny proměnné obsažené v~bloku):příkazyelse.
6.3.4
For cyklus
Jazyk E umožňuje používat for cykly. Nejedná se však o klasické for cykly známé z jazyků C/C++ či Pascal – ve skutečnosti jsou mnohem obecnější. Syntax for cyklu je následující: for(proměnné k~vyprázdnění, podmínka, příkaz) Běh for cyklu je možno popsat následovně: Na začátku každé smyčky jsou vyprázdněny proměnné, tj. do proměnných typu handle je dosazena volná proměnná, do (super)seznamu prázdný (super)seznam. Vlastní cyklus pak odpovídá cyklu s podmínkou na začátku, v němž – dokud je splněna podmínka – jsou spouštěny jednotlivé varianty příkazu. Spouštění podmínky a příkazu se opakuje do té doby, dokud obojí uspívá. Pokud první selže podmínka, for cyklus jako takový uspěje. Pokud však jako první selže příkaz, for cyklus neuspěje. Následující příkaz demonstruje použití for cyklu na výpisu prvků seznamu: vypisSeznam(sA):for(hPrvek, Member(hPrvek, sA), print(hPrvek)).
6.3.5
Repeat cyklus
Vždy splněný příkaz repeat lze s výhodou použít při konstrukci cyklů, je nutné ovšem použít :-- při definici skriptu (tedy povolit backtracking), jinak repeat uspěje pouze jednou, protože nesplněná podmínka, která by měla způsobit skok na repeat a v důsledku toho novou iteraci, zapříčiní selhání celého skriptu. Příkaz repeat se chová podobně jako predikát true – tento predikát je však splněn pouze (a právě) jednou. Příkaz true odpovídá prázdnému příkazu. Pro ukončení cyklu lze použít příkazy fail nebo false, které reprezentují vždy nesplněné podmínky. Příkazy fail a false jsou zcela ekvivalentní.
6.3.6
Atomické instrukce
Atomické instrukce umožňují entovi interagovat se svým okolím. Atomická instrukce jednoznačně popisuje cíl akce, typ akce a případné další parametry, které jsou pro danou akci požadovány. Jako parametry atomické instrukce je možné použít typ handle a typ seznam. Serveru prostředí jsou však parametry předány vždy jako jeden seznam objektů typu handle. V závislosti na aktuálním stavu světa může instrukce buď uspět, nebo selhat. O důvodu selhání je možné se dovědět z návratového kódu instrukce. Nulový
6.3. SKRIPTY JAZYKA E
71
návratový kód obecně indikuje úspěch instrukce, nenulový pak neúspěch. Podle hodnoty návratového kódu lze zjistit příčinu neúspěchu. Atomickou instrukci lze ve výkonném skriptu vyvolat příkazem: aInstrukce(parametry) kde aInstrukce představuje jméno instrukce (například aJdi), parametry pak seznam jejích parametrů. Seznam parametrů může být zapsán různými způsoby; následující příklad uvádí dva ekvivalentní: aOtri(hPredmet, hHadr) aOtri([hPredmet, hHadr]) Seznam atomických instrukcí s jejich podrobným popisem je uveden v příloze C (str. 129) . Návratový kód instrukce lze zjistit pomocí volání vestavěné funkce errorLevel. Tato funkce ukládá do proměnné návratový kód poslední atomické instrukce vyvolané konkrétní instancí skriptu, ve kterém je použita. Pokud tato instance od začátku své existence (nebo od posledního použití errorLevel) nezavolala žádnou atomickou instrukci, návratový kód je vždy 0. Funkce errorLevel vždy uspěje. Příklad použití funkce errorLevel: try aInstrukce(par1,par2), errorLevel(E), { {E==0, print("nedošlo k~žádné chybě")}; {E==1, print("došlo k~chybě číslo 1"), opravnaAkce1 }; {E==2, print("došlo k~chybě číslo 2"), opravnaAkce2 } }
6.3.7
Globální proměnné
Jazyk E podporuje globální proměnné. Jsou to proměnné, které jsou přístupny všem skriptům. Globální proměnné se deklarují v souboru se skripty následovně: Global: hMojeGlobalniPromenna; Global: mMujGlobalniMember; atd. Deklarace by se z důvodů přehlednosti neměla vyskytovat uvnitř těl skriptů, nýbrž mimo ně; nejlépe na začátku souboru se skripty. S globálními proměnnými v C/C++ je spojena jedna důležitá věc – překrývání, neboli shadowing. Jazyk E shadowing nemá, následující kód je tedy chybný:
72
KAPITOLA 6. JAZYK E
Global: X; funkce(X):-X=123. Správně to můžeme udělat například takto: funkce:-X=123. Tento kód v sobě stále skrývá jisté nebezpečí, protože proměnná X mohla být použita v řadě skriptů jako pomocná proměnná. Jak vyplyne dále v textu, skripty jazyka E se mohou přerušovat a tedy hrozí „race conditionsÿ, nehledě na to, že některé skripty mohou spoléhat na to, že dosud nepoužitá globální proměnná je vždy prázdná (volná).
6.3.8
Toplevel skript
Již jsme zjistili, že jedna funkce může volat druhou. Ve skriptech jazyka E musí vždy existovat jedna funkce, která je základní (”toplevel”). Toplevel funkce je analogií funkce main z C/C++. V E si můžeme vybrat název této funkce, doporučujeme ji však nazývat Zij. Označení funkce za toplevel vypadá následovně: Toplevel: Zij; Pokud jako toplevel deklarujeme více funkcí, bere se v potaz pouze první deklarace. Toplevel funkce může být pouze výkonný skript bez parametrů.
6.3.9
Používání více souborů, direktiva #include
Při psaní rozsáhlejších programů je vhodné jednotlivé části členit do více souborů. Dalším významným použitím mechanismu vkládání souborů je možnost psaní knihoven podprogramů. Jazyk E nabízí podobný mechanismus jako jazyk C, použití kódu v jiném souboru pomocí direktivy #include. Syntaxe je shodná s jazykem C, například: Soubor Zij.e: ... #include "chuze.e" #include "zalevani/slozite/dvema_konvemi.e" ... Soubor chuze.e: ... #include "pomocne.e" #include "db.e" ...
6.3. SKRIPTY JAZYKA E
73
Soubor zalevani/slozite/dvema konvemi.e: ... #include "konkretizuj.e" #include "db.e" ... Implementace se však od preprocesoru jazyka C liší. Preprocesor jazyka E nevkládá soubor na místo direktivy, ale připojuje ho na konec. Za předpokladu že výše zmíněný příklad neobsahuje již žádné další direktivy #include, tak výsledkem preprocesoru bude zřetězení souborů v pořadí: Zij.e, chuze.e, zalevani/slozite/dvema konvemi.e, pomocne.e, db.e, konkretizuj.e. Vkládané soubory hledá preprocesor podle nastavení proměnné prostředí $E INCLUDE. Pokud není jméno vkládaného souboru uvedeno s absolutní cestou, prochází preprocesor postupně všechny adresáře uvedené v $E INCLUDE (oddělovačem je dvojtečka) a hledá soubor v nich. Obyčejně je prvním z adresářů je ./, aktuální adresář, a dále ./skripty/, tj. adresář skriptů. Je však třeba upozornit na to, že interpret skriptů pracuje zásadně v adresáři balíku světa. Identifikujte proto vkládané soubory nejlépe relativně k balíku světa. Preprocesor E na rozdíl od preprocesoru C navíc automaticky kontroluje, zda nebyl soubor stejného jména již dříve vložen. Soubor db.e v našem příkladu výše bude proto použit pouze jednou. To ovšem neznamená, že není možné jeden fyzický soubor vložit do výsledku dvakrát. Toho je možné dosáhnout jednak pomocí soft a hard linků – soubor bude mít dvě různá jména a preprocesor nic nepozná – a jednak nesprávným použitím proměnné $E INCLUDE, jak ukazuje následující příklad: E_INCLUDE=~/ei/scripts:~/ei/scripts/myscripts Soubor Zij.e: ... #include "mojefunkce.e" #include "myscripts/mojefunkce.e" ... Výsledný soubor bude obsahovat soubor mojefunkce.e dvakrát. Skripty definované v tomto souboru budou mít proto dvakrát více variant, což může za běhu působit potíže.
6.3.10
Použití databáze enta
Databáze enta slouží především k uchovávání jeho znalostí o okolním světě, ale můžeme ji použít i pro naše výpočty. Databáze je však limitovaná tím, že používá pouze typ handle – pokud tedy chceme uchovávat jiné typy, musíme použít globální proměnné. Pro ukládání informací do databáze slouží tzv. fakta. Fakta obecně odpovídají n-árním predikátům. Fakta je nejprve nutné v rámci skriptu (nejlépe na
74
KAPITOLA 6. JAZYK E
začátku souboru) definovat pomocí příkazu: Fact: Jméno; kde Jméno reprezentuje jméno vytvářeného faktu. Pro pojmenovávání faktů doporučujeme použít jmennou konvenci faktJméno, díky které budou fakta snadno odlišitelná od ostatních predikátů používaných ve skriptech. Konkrétní fakta lze do databáze enta ukládat pomocí příkazu set: set faktKamaradi(hEnt1, hEnt2) Výše uvedený příkaz vloží do databáze fakt o dvou parametrech, který říká, že hEnt1 a hEnt2 jsou kamarádi. Rušit fakta pak lze prostřednictvím příkazu del. Následující implementace skriptu fib demonstruje reálné použití faktů. Oproti předchozí verzi skriptu, která měla exponenciální složitost, tento je již lineární: // fib(h+,h-) Fact: faktfib;
// založíme faktfib jako nový fakt v~databázi
fib(N,F):F=_, faktfib(N,F), // ptáme se, zda již známe řešení !. fib(N,F):N==0, F=1, set faktfib(N,F). // ukládáme řešení do databáze fib(N,F):N==1, F=1, set faktfib(N,F). // ukládáme řešení do databáze fib(N,F):N>1, fib(N-1,F1), fib(N-2,F2), F=F1+F2 set faktfib(N,F). // ukládáme řešení do databáze Toto řešení má jistý háček, neboť po zavolání skriptu fib zůstávají v databázi spočítaná řešení. To nevadí u Fibonacciho posloupnosti, ale může to vadit u výpočtů, které jsou závislé na dalších parametrech nebo na ostatních faktech v databázi. Řešením je obalit skript fib dalším skriptem, který bude uložené údaje po skončení výpočtu mazat. K tomu lze s výhodou využít for cyklu:
6.3. SKRIPTY JAZYKA E
75
fib2(N,F):fib(N,F), sN=[], // do seznamu uložíme první parametr // instancí predikátu faktfib for(x,y,faktfib(x,y),sN+=x), for(x,y,Member(x,sN), // vlastní mazání známých faktů z~databáze { faktfib(x,y),del faktfib(x,y) }) . Dva for cykly byly použity zcela záměrně, přestože by je na první pohled bylo možné sloučit do jednoho. Důvodem, proč to nelze udělat, je to, že bychom měnili strukturu, přes kterou for cyklus běží. Následující kód by tedy vymazal pouze faktfib pro čísla 0, 2, 4, . . . : fib2(N,F):fib(N,F), for(x,y,faktfib(x,y),del faktfib(x,y)). Stejné chyby se můžeme dopustit i při for cyklu nebo backtrackingu přes seznam. Následující kód obsahuje je tedy také nesprávý: for(x, Member(x,sA), { sA-=x, print("Vyhazujeme ze seznamu ", x) }) Programátor skriptů v jazyce E jen zřídka využívá databázi přímo. Ve většině případů lze totiž použít sadu standardních skriptů obsažených v knihovně db.e (7.3(str. 96) ).
6.3.11
Funkce findAll
V předchozím případě s Fibonacciho čísly jsme ke sbírání řešení používali for cyklus, nicméně jazyk E pro tyto účely disponuje specializovaným nástrojem: predikátem findAll, jehož syntax je následující: findAll(sbíraná proměnná, podmínka, (super)seznam řešení) Příkaz findAll pracuje následujícím způsobem: Na začátku vyprázdní (super)seznam sbíraných řešení. Poté v každém cyklu vyprázdní sbíranou proměnnou (obdobně jako for cyklus) a spustí podmínku. Pokud podmínka neuspěje, celý findAll skončí úspěchem. Pokud podmínka uspěje, findAll řešení přidá do super(seznamu) a nastartuje další cyklus. Jak je vidět, findAll vždy uspěje. Pomocí příkazu findAll můžete sbírat handly do seznamu, nebo membery do superseznamu. Mazání Fibonacciho čísel z databáze můžeme naprogramovat pomocí findAll následovně:
76
KAPITOLA 6. JAZYK E
fib2(N,F):fib(N,F), // do dvou seznamů si uložíme parametry // instancí predikátu faktfib findall(x,faktfib(x,_),sN), for(x,y, Member(x,sN), // vlastní mazání známých faktů z~databáze { faktfib(x,y), del faktfib(x,y) }) .
6.3.12
Problémy se složenými podmínkami – poznámka o architektuře
Při pohledu na první řešení skriptu fib2 by se mohlo zdát, že by mohl správně fungovat i následující postup: fib2(N,F):fib(N,F), sN=[], sF=[], for(x,y,faktfib(x,y), // do dvou seznamů si uložíme parametry // instancí predikátu faktfib { sN+=x, sF+=y } ), for(x,y,{ Member(x,sN), Member(y,sF) }, // vlastní mazání známých faktů z~databáze del faktfib(x,y) ) . Toto řešení v E nefunguje (v Prologu by však fungovalo), protože E a Prolog mají rozdílnou vnitřní strukturu. Prolog si ukládá splněné klauzule na zásobník, kdežto E má strukturu podobnou nedeklarativním jazykům a na zásobník ukládá celé volání skriptu (funkce), ne jednotlivé klauzule. Jeden predikát (funkce) v Prologu může vydat více řešení. Naproti tomu v E nelze obdržet více jak jedno řešení na variantu skriptu (jistou možnost skýtá příkaz call, viz kapitola 6.3.16(str. 80) ). Databáze E však funguje v tomto ohledu stejně jako ta prologovská. V několika předchozích příkladech jsme viděli příkazy obalené složenými závorkami . V popisu konstrukce if-then-else byly zmíněny generované skripty. Vzhledem k tomu, že složené příkazy lze do sebe libovolně vnořovat, a tomu, že E disponuje prostředky neobvyklými v jiných jazycích, je nutné, aby interpret skriptů nebyl přiliš složitý. Složené příkazy se převádí na volání generovaných skriptů. Pokud použijeme jako podmínku složený příkaz, jako podmínka bude použit generovaný skript, který má pouze jednu variantu – takže výše uvedená implementace skriptu fib2 smaže pouze první řešení faktu faktfib. Tento problém lze obejít více způsoby. Prvním je již diskutované použití dvou for cyklů, kdy seznam řešení prvního for cyklu hraje roli jako řídící struktura pro for cyklus druhý.
6.3. SKRIPTY JAZYKA E
77
Druhým způsobem je posbírání n-tic řešení do superseznamu, jak ukazuje následující příklad: fib2(N,F):fib(N,F), ssNF=[[]], for(x,y,mA,faktfib(x,y), { mA["special"]=x, mA["special1"]=y, ssNF+=mA } ), for(mA, Member(mA,ssNF), del faktfib(mA["special"], mA["special1"]) ). Poznámka: generované skripty dědí typ backtrackingu svého předka. Ještě jednou Fibonacciho čísla – použití globální proměnné místo databáze Následující příklad demonstruje použití globální proměnné namísto databáze při výpočtu Fibonacciho čísel: // fib(h+,h-) Global: ssFibonnaci; // deklarujeme globální proměnnou fib(N,F):F=_, nactiFib(N,F), !. fib(N,F):N==0, F=1, ulozFib(N,F). fib(N,F):N==1, F=1, ulozFib(N,F). fib(N,F):N>1, fib(N-1,F1), fib(N-2,F2), F=F1+F2 ulozFib(N,F). nactiFib(N,F):-Member(mX,ssFibonnaci), mX["special"]==N, F=mX["special1"].
78
KAPITOLA 6. JAZYK E
ulozFib(N,F):ssFibonnaci+=[ "special"=N : "special1"=F ]. Poslední příklad týkající se Fibonacciho čísel ukazuje, jak nahradit globální proměnnou pomocí proměnné předávané referencí v obalovacím skriptu: // fib(h+,h-) fib2(N,F,~ssFibonnaci):F=_, nactiFib(N,F,ssFibonnaci), !. fib2(N,F,~ssFibonnaci):N==0, F=1, ulozFib(N,F,ssFibonnaci). fib2(N,F,~ssFibonnaci):N==1, F=1, ulozFib(N,F,ssFibonnaci). fib2(N,F,~ssFibonnaci):N>1, fib(N-1,F1), fib(N-2,F2), F=F1+F2, ulozFib(N,F,ssFibonnaci). nactiFib(N,F,~ssFibonnaci):-Member(mX,ssFibonnaci), mX["special"]==N, F=mX["special1"]. ulozFib(N,F,~ssFibonnaci):ssFibonnaci+=[ "special"=N : "special1"=F ] fib(N,F):fib2(N,F,[[]]).
6.3.13
Volání C funkcí
Přestože je jazyk E poměrně silným programovacím nástrojem, má své limity a nevýhody. Proto je vhodné mít rozhraní, které dovolí napojení na moduly z jiných programovacích jazyků. Jazyk E podporuje volání funkcí napsaných v C/C++ a jiných jazycích (např. Mercury), které mají C/C++ interface pro
6.3. SKRIPTY JAZYKA E
79
volání (příslušná rozšíření však musí být přirozeně již kompilována do interpretu jazyka E, tj. nejsou podporovány dynamické knihovny C funkcí). Seznam a popis všech C-funkcí je k dispozici v kapitole Vestavěné funkce, 7.1(str. 92) . Následující příklad demonstruje volání C-funkce cRound vracející ve svém parametru počet kol od začátku dne: pocetKol(X):cRound(X).
6.3.14
ISA vztah
Modelovaný svět obsahuje řadu objektů, z nichž některé se sdružují do skupin. Toto sdružování může být hierarchické a nazývá se ISA vztah, z anglického „is aÿ. Například mrkev je zelenina. V jazyku E to lze vyjádřit predikátem: ISA("mrkev","zelenina") Stejně tak, zelenina je jídlo: ISA("zelenina","jidlo") ISA vztah je vlastně speciální predikát uložený v entově databázi, který nelze modifikovat. Obsah tohoto predikátu závisí na naplnění databáze. Je možné se zeptat i na to, jestli daný vztah neplatí: ISNOT("nuz","jidlo")
6.3.15
Negace
Většinu podmínek je možné negovat pomocí logické spojky NOT nebo not. Je možné negovat volání výpočtového skriptu, volání C-funkce nebo operaci s databází. Lze také negovat složený příkaz. Není dovoleno negovat výkonný skript, protože to by znamenalo v případě jeho neúspěchu „odčinitÿ případné atomické instrukce, což není možné. Z důvodu redundance není dovoleno negovat porovnávání a speciální predikát ISA/ISNOT. Logická spojka negace má dvě implementace – NOT a not –, které se chovají stejně ve skriptech, v nichž není povolen backtracking (tj. v těch skriptech, jejichž tělo je uvozeno operátorem :-). Ve skriptech, které backtracking používají (tj. jejich tělo je uvozeno operátorem :--), se tyto dvě negace liší následovně: Obě znegují hodnotu úspěchu příkazu, liší se však tím, že not zakáže možnost dalších řešení daného příkazu, zatímco NOT nikoliv. Rozdíl ukáže následující příklad:
80
KAPITOLA 6. JAZYK E
skript:-true. skript:-false. UspesnySkript:-NOT skript. NeuspesnySkript:not skript. Pokud bychom prohodili varianty skriptu skript, NeuspesnySkript by také uspěl. Pokud používáme backtracking a chceme použít negaci, ve většině případů použijeme malé not.
6.3.16
Funkce Call
Konstrukt call se podobá call z jazyka Prolog, je však silnější. Call slouží ke dvěma účelům: jednak ke zjištění hodnoty ohodnocovací funkce daného skriptu při daných parametrech a jednak k samotnému volání skriptu. Zjišťování hodnoty ohodnocovací funkce pro dané parametry lze využít v situaci, kdy se jednotlivé varianty akce neliší tělem skriptu, ale parametry. Pokud je množina parametrů neměnná a předem známá, lze to to obejít vytvořením variant lišících se konstantami, pokud ovšem množina parametrů není při psaní skriptu či spuštění známa, tak je nutné se uchýlit ke složitým a nepřehledným konstrukcím. Call umožňuje tyto problémy řešit elegantně a přehledně. Syntax příkazu call pro zjištění hodnoty ohodnocovací funkce skriptu je následující: call("název skriptu", hodnota)(parametry) První parametr příkazu call je jméno ohodnocovaného skriptu v uvozovkách. Druhým parametrem je proměnná, do níž má být uložena hodnota ohodnocovací funkce tohoto skriptu. Při volání ohodnocovací funkce předá call skriptu udané parametry. Pokud chceme použít příkaz call pro spuštění skriptu, je syntax obdobná, chybí jen parametr hodnota: call("název skriptu")(parametry) Příklad použití call pro výběr nejlepší varianty – chceme vybrat nejlepší potravinu ke snězení: snezPotravinu(hPotravina)$ISA(hPotravina,"jidlo"), // hPotravina musí být jidlo stav(hPotravina,"kalorie",KAL), // stav vrátí pro dany předmět počet kalorií RAD=0, // pro pripad, že nevím jak to mám rad, tak střední hodnotu
6.3. SKRIPTY JAZYKA E
81
try faktMamRad(hPotravina,RAD), // RAD je v rozmezí -20 až 20, vetší je lepší return 2*RAD+7*KAL. :..... snezNejlepsiPotravinu(~sPotraviny)$return 0. :ssOhodnocene=[[]], for(X,Member(X,sPotraviny), { call("snezPotravinu",EVAL)(X), if EVAL>nINF then ssOhodnocene+=["predmet"=X : "special"=EVAL] fi }), ssSort(ssOhodnocene,"special"), Member(mX,ssOhodnocene), snezPotravinu(mX["předmět"]). Při zjišťování hodnoty ohodnocovací funkce call vždy uspěje, i když ohodnocovací funkce selže. Tehdy call dosadí do proměnné hodnotu nINF (mínus nekonečno). Není tedy vhodné, aby ohodnocovací funkce vracely hodnotu nINF, pokud na ně chceme použít call. Následující příklad demonstruje použití call při výběru cílů. Jedná se o jednoduchý rozvrhovač, který vybere vždy ten cíl, který je nejvýhodnější: Toplevel Zij; Zij$return 0. :-sCile=["zalij"+"spi"+"jez"+"osprchujSe"+"hrajHru"+"ctiKnihu"], ssTrideneCile=[[]], repeat, for(Cil,Member(Cil,sCile), { call(Cil,EVAL), if EVAL>nINF then ssTrideneCile+=["predmet"=Cil:"special"=EVAL] fi }), ssSort(ssTrideneCile,"special"), Member(mX,ssTrideneCile), ssTrideneCile=[[]], call(mX["predmet"]), // tento call uspěje právě tehdy, // uspěje-li volaný cíl fail.
82
KAPITOLA 6. JAZYK E
6.3.17
Funkce měření reálného času
Zatímco čas ve světě modelovaném projektem Enti běží diskrétně, pro vývojáře může být důležité mít přehled o tom, kolik času stojí výpočty, pomocí nichž ent generuje své akce. Jazyk E pro tyto účely obsahuje příkazy pushtime a poptime. Čas se měří vždy v rámci instance skriptu a měření se vypisují na standardní výstup. Funkce pushtime uloží do instance skriptu časovou značku odpovídající reálnému času. Před prvním voláním pushtime není obsah časové značky definován. Funkce poptime se zeptá na aktuální čas a vypíše časový rozdíl mezi tímto časem a uloženou časovou značkou. Čas se měří s přesností na mikrosekundy. Příklad: jak dlouho trvá spočítat fibonacciho číslo 20? ..., pushtime, fib(20,f20), poptime, print("fibonacciho cislo 20 je rovno ",f20), ...
6.3.18
Podpora multitaskingu
Jazyk E byl navržen jako prostředek pro simulaci člověku podobných inteligentních bytostí. Člověk je tvor, který vykonává řadu činností. Nelze však říci, že by tyto činnosti vykonával způsobem, že si jednu vybere, dokončí ji a pak si vybere další atd. Člověk má běžně rozděláno několik činností, mezi kterými „přepínáÿ. Přepínání závisí na okolních podmínkách. Přepínání může být také lokální a globální. Uvažujme následující situaci: hospodyně vaří v kuchyni oběd a právě krájí cibuli. Příkladem lokálního přepnutí je ztlumení vařiče pod klokotajícím hrncem, globálním přepnutím může být přijetí rekomanda od pošťáka. Vaření oběda a komunikace s pošťákem jsou dvě různé úlohy, ztlumení vařiče byla akce příslušející do úlohy vaření oběda. Pokud se podíváme na to, jak vypadá instance některého programu v jazyce C v paměti, tak volání jednotlivých funkcí jsou reprezentovány strukturami na zásobníku. Pokud bychom použili pouze dosud popsané nástroje jazyka E, tak by každý běh programu byl rovněž reprezentován zásobníkem. Jazyk E však disponuje mechanizmem tzv. interruptů, který umožňuje spouštění skriptů na základě nějaké podmínky kontrolované za běhu. Diky tomuto se nám zásobník změní na strom. Možnost tvoření nezávislých úloh ze stromu vytváří les. Les je oproti zásobníku mnohem složitější struktura, návrh komplexnější simulace v E je proto náročnější na rozvržení jednotlivých skriptů, než návrh mnoha programů v obvyklých programovacích jazycích. K tomu, abychom pochopili, jak interrupty fungují, je nutné napřed osvětlit, jak se vlastně rozhoduje, který skript poběží. Simulace v jazyce E běží po kolech. V každém kole každá entita posílá atomickou instrukci, přičemž mezi instrukcemi provádí výpočty. Podstatnou věcí je to, že pokud se strom skriptů nevzdá řízení, nebo dokud nepošle atomickou instrukci (nebo neskončí), tak není
6.3. SKRIPTY JAZYKA E
83
možné mu odebrat procesor. Máme tedy spuštěno více „úlohÿ, na začátku každého kola se rozhoduje, která úloha bude toto kolo běžet. Mechanismem, který toto rozhodování řídí jsou tzv. priority.
6.3.19
Priority
Priority existují dvojího druhu, lokální a globální. Globální priority rozhodují o tom, která úloha/strom poběží, lokální rozhodují, který list vybraného stromu poběží. Priority jsou čísla z aritmetiky jazyka E. Nejmenší priorita je tedy nINF, nejvyšší pINF, výchozí hodnota je 0. Toto platí jak pro globální, tak pro lokální priority. Pokud více než jedna úloha má okamžiku rozhodování maximální (globální) prioritu, je mezi nimi z pohledu uživatele vybráno náhodně. Totéž platí pro shodné maximální lokální priority. Těmto situacím je vhodné se vyhnout, i když chování enta, který v lichém kole chce jít do pravých dveří a sudém do levých, může být zábavné pozorovat. Priority se nastavují a zjištují následujícími příkazy: • setLocalPriority(X) – nastaví lokální prioritu na obsah proměnné X • getLocalPriority(X) – do X uloží číslo odpovídající lokální prioritě • setGlobalPriority(Start, Height, Drop, Deadline) – nastavení lichoběžníku popisujícího globální prioritu (viz níže) • getGlobalPriority(Start, Height, Drop, Deadline) – zjištění lichoběžníku popisujícího globální prioritu (viz níže) • getGlobalPriority(X) – do X uloží číslo odpovídající aktuální globální prioritě Lokální priorita je vždy konstantní, hodnota globální priority může záviset na čase. Parametry Start, Height, Drop a Deadline funkcí getGlobalPriority a setGlobalPriority charkterizují lichoběžník popisující průběh globální priority v závislosti na čase (viz obrázek 6.1(str. 84) ). Aktuální globální priorita je rovna výšce lichoběžníka v daném čase. Priorita před časem Start je nulová, stejně tak jako po čase Deadline Parametry Start, Drop a Deadline jsou udávány v kolech od začátku dne (ne od začátku simulace), protože entův život by měl odpovídat denním cyklům. Aktuální kolo dne je možná zjistit pomocí C-funkce cRound. Označme AK aktuální kolo a GP globální prioritu. Potom:
AK < Start ⇒ GP = 0 AK ≤ Drop ⇒ GP = Height AK ≤ Deadline ⇒ GP = Height ∗ (Deadline − AK)/(Deadline − −Drop) jinak ⇒ GP = 0
84
KAPITOLA 6. JAZYK E priorita 6 Height
@
Start
@
@
Drop
@
@
Deadline
-
čas
Obrázek 6.1: Popis globální priority pomocí lichoběžníku
6.3.20
Interrupty
Interrupt v jazyku E je konstrukce sloužící ke spuštění skriptu, který má reagovat na situaci asynchronně, tedy něco podobného jako interrupt reagující na stisk klávesnice vašeho PC nebo na pohyb myší. Tuto konstrukci ovšem můžete použít také k vyvolávání skriptů synchronně. Existují celkem 4 druhy interruptů, které můžeme rozdělit do dvou skupin – na interrupty lokální a na interrupty globální. Ztlumení vařiče během vaření je lokální interrupt, konverzace s poštákem interrupt globální. Lokální interrupt přidá větev stromu odpovídající úlohy, globální interrupt zasadí další strom do stávajícího lesa úloh. Interrupty dědí proměnné volajícího skriptu, nebo k těmto proměnným mají přímý přístup. Lokální interrupty mají dvě podoby, které jsou analogické předávání proměnných volanému skriptu (funkci), tedy předávání odkazem a hodnotou. Interrupt je vlastně také skript a může buď přejmout kopii prostoru proměnných svého mateřského skriptu v okamžiku svého vytvoření, nebo přistupovat na proměnné přímo do paměťového prostoru mateřského skriptu. Na rozdíl od interruptů lokálních, globální interrupty si vždy vytvářejí kopii prostoru proměnných. Každý interrupt má svůj identifikátor, který je mu přidělen v okamžiku jeho vytvoření. Identifikátor se ukládá do proměnné, která je naplněna jak v prostoru skriptu, tak v prostoru instance interruptu. (Lze tedy v interruptu pracovat s identifikátorem sama sebe.) Při vytvoření interruptu (tzv. navěšení) se vytvoří šablona, která se po dobu svého života může libovolněkrát instanciovat (na základě podmínky interruptu), přičemž při instanciaci se vždy vytváří kopie šablony. Pokud interrupt nepoužije databázi nebo globální proměnnou, nemůže zjistit, kolikrát se již instancioval a šablona je tedy od vytvoření neměnná. Globální interrupty se mohou instanciovat tehdy, je-li jejich podmínka splněna a všechny ostatní běžící úkoly mají nižší prioritu. Lokální interrupt se může instanciovat, pokud jeho úloha je aktivní (tj. právě se provádí), jeho podmínka je splněna a jeho priorita je vyšší než priorita ostatních listů úlohy. Počet navěšených interruptů v systému je omezen číslem 65524, počet instancí omezen není. Obecná syntax pro použití interruptů je následující:
6.3. SKRIPTY JAZYKA E
85
typInterruptu(podmínka, akce, priorita, identifikátor [, identifikační řetězec]) kde typInterruptu udává typ navěšovaného interruptu (viz níže), podmínka představuje podmínku, po jejímž splnění se aktivuje akce. Priorita interruptu je určena parametrem priorita. Po navěšení interruptu je jeho identifikace uložena do proměnné identifikátor. Volitelně lze interrupt též pojmenovat identifikačním řetězcem (např. pro účely ladění). Lokální interrupt s kopií proměnných Syntax: hook(podmínka, akce, priorita, identifikátor [, identifikační řetězec]) Příklad: hook(mamHlad, snezNeco, 100, intHladID, "osetreni hladu") hook(mamZizen, napijSe, 110, intZizenID) Lokální interrupt s odkazem na proměnné Syntax: localHook(podmínka, akce, priorita, identifikátor [, identifikační řetězec]) Příklad: localHook(mamHlad, snezNeco, 100, intHladID, "osetreni hladu") localHook(mamZizen, napijSe, 110, intZizenID) Globální interrupt Syntax: globHook(podmínka, akce, priorita, identifikátor [, identifikační řetězec]) Příklad: globalHook(mamHlad, snezNeco, 100, intHladID, "osetreni hladu") globalHook(mamZizen, napijSe, 110, intZizenID)
86
KAPITOLA 6. JAZYK E
Sleep interrupt Syntax: sleepHook(podmínka, identifikátor [, identifikační řetězec]) Příklad: sleepHook(mamHlad, snezNeco, intHladID, "osetreni hladu") sleepHook(mamZizen, napijSe, intZizenID)
Lokální interrupty slouží především jako rutiny hlídající jisté invarianty, které jsou potřebné pro zdárné dokončení akce, globální interrupty se hodí na vytváření nových úloh. Pokud dojde k selhání lokálního interruptu, dojde k selhání jeho vlastníka. Je tomu tak proto, že selhání interruptu znamená neplatnost nějakého invariantu nutného pro běh vlastníka. Například pokud se hospodyni nepodaří ztlumit vařič pod hrncem, tak selže celé vaření. Sleep interrupt se spouští stejně jako globální interrupt, tedy před spuštením úlohy, ale má předka. Jeho účelem však není vytvářet nové úlohy, nýbrž je budit. Úloha se může rozhodnout, že je lepší počkat (mohla by sice posílat atomické instrukce NOP, ale tím by sama nic nezískala a navíc by bránila ostatním úlohám v běhu). Proto se může uspat. Uspání je možné na daný počet kol, nebo na neurčito. K probuzení slouží sleep interrupty, které vzbudí svého vlastníka v případě, že je splněna jejich podmínka. Tuto podmínku je možné splnit uměle pomocí volání funkce wakeup. Tato funkce má jeden parametr, jímž je identifikátor sleep interruptu, který má probudit svého majitele. Pokud skript zavolal funkci sleep a navěsil několik sleep interruptů, tak může pomocí příkazu wokenupby zjistit identifikátor interruptu, který ho vzbudil. Následující příklad demonstruje jednoduché hlídání hladu a žízně: sleepHook(mamHlad,intHlad), sleepHook(mamZizen,intZizen), sleep(50), wokenupby(intWho), if intWho === intHlad then print("mamHlad") else if intWho === intZizen then print("mamZizen") else if intWho === _ then print("vyprsel timeout") else print("chyba")
6.3. SKRIPTY JAZYKA E
87
fi fi fi
6.3.21
Blokování interruptů
Někdy může být vhodné interrupt na chvíli zablokovat a potom zase povolit. K tomu potřebujeme znát jeho identifikátor. Blokovat můžeme pouze neblokovaný interrupt a povolovat naopak pouze interrupt, který je blokovaný. Povolení neblokovaného interruptu selže, stejně jako blokování blokovaného. Pro blokování a povolování interruptů lze použít funkce block a enable. Obě tyto funkce mají jeden parametr, jímž je identifikátor interruptu. Příklad blokování interruptů: ... block(intID), ... enable(intID), ...
6.3.22
Rušení interruptů
V některých situacích je nutné se interruptu úplně zbavit. Toho lze docílit tak, že zrušíme jeho šablonu a tím zabráníme případnému vytvoření další instance. Zrušit interrupt může kdokoli, kdo zná jeho identifikátor. Interrupty se přidělují cyklicky, takže pokud si uložíme nějaký identifikátor interruptu a pak tento interrupt zrušíme (ale jeho identifikátor máme dále uschovaný) a mezitím vytvoříte dalších asi 65 tisíc interruptů, původní identifikátor interruptu se může vztahovat k něčemu úplně jinému. Interrupty lze rušit pomocí příkazu unhook, jehož parametrem je identifikátor interruptu. Pokud je již identifikátor neplatný, unhook selže; jinak zruší interrupt a uspěje. Příklad: interrupt, který se aktivuje pouze jednou ... hook(mamHlad, { unhook(intHlad), ... }, 100, intHlad, "interrupt hlad na jedno použití"), ... Upozornění Pokus o aritmetické operace na identifikátorech interruptů je považován za vážnou chybu a vede k okamžitému ukončení interpretu.
88
KAPITOLA 6. JAZYK E Přidávání do seznamu, kopírování atd. nejsou v tomto smyslu aritmetické operace a jsou povoleny.
6.3.23
Funkce řízení běhu
Vzhledem k tomu, že struktura programů v jazyce E je relativně složitá, jsou nezbytné některé poněkud netradiční nástroje. Prvním z těchto nástrojů je příkaz COMMIT, který způsobí okamžitý úspěch skriptu. Pokud bychom se na skripty v jazyce E dívali jako na na funkce v C tvaru: Bool Skript(...), pak COMMIT je analogický příkazu return true. COMMIT je však mnohem silnější. Použití příkazu ve tvaru COMMIT(n), kde n je číslo (nikoli proměnná), způsobí úspěch n předků volajícího skriptu1 . COMMIT(1) tedy způsobí okamžitý úspěch skriptu i jeho předka; COMMIT(2) skriptu, předka a prapředka; atd. Kromě toho, že jazyk E obsahuje analogii return true, obsahuje i analogii return false, kterou je funkce FAIL – nezaměňovat s fail, protože FAIL a fail jsou dva zcela rozdílné příkazy, i když se při nepoužití backtrackingu FAIL a FAIL(0) chovají shodně jako příkaz fail či false. COMMIT a FAIL se liší svým chováním vzhledem k interruptům. Pokud skript uspěje, tak se jeho interrupty předávají rodičovskému skriptu. Předávat se mohou pouze interrupty s vlastníkem – tedy ty, které byly navěšené navěšené pomocí volání hook a sleepHook. Interrupty navěšené pomocí volání globHook nemají vlastníka a tedy na ně běh jiných skriptů nemá vliv (pokud někdo samozřejmě nezavolá unhook). Interrupty navěšeně pomocí volání localHook sice mají vlastníka, ale nelze je předat, protože jejich datové položky (proměnné i konstanty) jsou v datovém prostoru skriptu, který bude právě likvidován a jeho pamět uvolněna. Pokud skript selže, jeho interrupty se likvidují. Nelikvidují se však jenom interrupty, likviduje se celý podstrom skriptů, tj. také veškeré instance, jejichž předkem (rodičem, prarodičem, . . . ) je selhavší skript. Zjednodušeně lze říci, že COMMIT předá svůj podstrom rodiči, zatímco FAIL svůj podstrom zlikviduje. Dalšími dvěma příkazy řízení běhu jsou příkazy RERUN a RERUN AGAIN. Chovají se podobně jako FAIL, avšak s jedním podstatným rozdílem: Po neúspěchu skriptu a provedení výše uvedených akcí je varianta skriptu spuštěna znovu se stejnými parametry, pokud ovšem některé nebyly předávané odkazem a předchozí běh je nemodifikoval. RERUN spouští přesně tu variantu, která předtím selhala. Pokud však chceme spustit znovu proces výběru varianty pomocí ohodnocovacích funkcí, použijeme RERUN AGAIN. RERUN i RERUN AGAIN lze použít s parametrem hloubky stejně jako u příkazů COMMIT a FAIL. Přestartování skriptu provádí jeho rodič, tedy není možné přestartovat kořen stromu pomocí RERUN. Pokud je to přesto nezbytné, lze toho dosáhnout například pomocí repeat cyklu, nebo globální interruptem pro znovuspuštění úlohy. 1 COMMIT(0)
je ekvivalentní volání COMMIT.
6.3. SKRIPTY JAZYKA E
6.3.24
89
Zjišťování ohodnocení a priority
Skript může zjistit hodnotu, kterou vrátila jeho užitková funkce pomocí volání c getUc. Tato funkce očekává jeden parametr, do něhož uloží hodnotu ohodnocovací funkce. Pro zjišťování aktuální priority lze použít funkce getLocalPriority (pro lokální prioritu) a getGlobalPriority (pro globální prioritu). Obě funkce byly popsány v kapitole 6.3.18(str. 82) . Běžící úloha či interrupt může zjistit nejvyšší globální prioritu mezi ostatními běžícími úlohami (ne interrupty) pomocí funkce topUtility. Všechny zmíněné funkce vždy uspějí.
6.3.25
Vzdání se řízení
Úloha může přijít o řízení pokud: • vykoná atomickou instrukci a její priorita již není maximální nebo • skončí. Někdy však může být vhodné vzdát se řízení i v jiných případech – například tehdy, kdy si úloha změnila (snížila) globální prioritu a chce, aby to mělo okamžitý efekt. Tohoto je možné dosáhnout příkazem reconsider, který nemá žádné parametry a vždy uspěje.
90
KAPITOLA 6. JAZYK E
Kapitola 7
Knihovna E V této kapitole najdete přehled všech knihoven, které jsou součástí standardní distribuce projektu Enti. Základní funkce jsou pro přehlednost rozděleny do několika samostatných knihoven. Vestavěné funkce Některé funkce pro manipulaci s handly ap. jsou již vestavěny v interpretu. Popis vestavěných funkcí najdete v kapitole 7.1(str. 92) . Vestavěné funkce zásadně negenerují atomické instrukce. seznamy.e Knihovna pro základní operace se seznamy a superseznamy. Funkce této knihovny nikdy negenerují atomické instrukce. db.e Tato knihovna obsahuje funkce pro snadné vybavování si konkrétních předmětů a získávání dalších údajú z databáze. Programátor využije služeb této knihovny prakticky vždy, protože ve světě entů je třeba manipulovat s konkrétními předměty, ovšem konkrétní čísla předmětů je velmi nevhodné uvádět do skriptů přímo. Proces konkretizace ve skriptu umožňuje vybavit si, jaké konkrétní konve např. ent viděl. Funkce této knihovny nikdy negenerují atomické instrukce. chuze.e Knihovna obsahuje základní funkce pro přecházení mezi místnostmi, přecházení k předmětům. Funkce této knihovny často generují atomické instrukce. predmety.e Knihovna obsahuje funkce pro jednoduché manipulace s předměty. Hledání předmětů, sbírání a pokládání předmětů. Funkce této knihovny často generují atomické instrukce. enti.e Knihovna obsahuje funkce pro jednoduchou kooperaci s dalšími enty. Funkce na hledání enta, sledování enta, či dojití k entovi. Funkce této knihovny často generují atomické instrukce. lingvistika.e Knihovna obsahuje interrupty pro vyslovování a poslech vět. 91
92
KAPITOLA 7. KNIHOVNA E
rozvrhovac.e Knihovna obsahuje implementaci jednoduchého rozvrhovače úloh včetně lingvistivké podpory. zivotnifunkce.e Knihovna obsahuje implementaci funkcí obhospodařujících základní životní potřeby entů. debugging.e Knihovna obsahuje funkce užitečné zvláště pro potřeby ladění skriptů. Funkce usnadňí připravit ve světě konkrétní situaci. (Provést enta nějakými místnostmi ap.) Funkce mohou i nemusí generovat atomické instrukce. Knihovní funkce, které generují atomické instrukce, jsou v následujícím přehledu označeny AI . Tyto funkce jsou do určité míry připraveny na možné komplikace způsobené změnami prostředí v průběhu plnění daného úkolu. V popisu funkcí najdete úplný výčet výjimečných situací, s nimiž funkce počítá.
7.1 7.1.1
Vestavěné funkce sametype(+hHandle1, +hHandle2)
NOAI
Uspěje, pokud mají argumenty stejný typ.
7.1.2
striptoken(+hHandle, -hTyp)
NOAI
Ve druhém parametru vrací daný handle, ovšem s vynulovanou koncovkou. Je možné použít též ve variantě striptoken(±hHandle), kdy upraví rovnou zadanou proměnnou.
7.1.3
applymaintype(+UkazkaMainTypu, ±hHandle)
NOAI
Ve druhém parametru nastaví hlavní typ podle hlavního typu prvního parametru. Zbytek hodnoty handlu nechá beze změny.
7.1.4
cExtractTile(+hDlaz, -hMistnost, -X, -Y)
NOAI
Funkce rozloží handle dané dlaždice na handle místnosti a souřadnice v místnosti.
7.1.5
cCreateTile(+hMistnost, +X, +Y, -hDlaz)
NOAI
Funkce z handlu udané místnosti a daných souřadnic vytvoří handle odpovídající dlaždice.
93
7.1. VESTAVĚNÉ FUNKCE
7.1.6
cJa(-hJa)
NOAI
Funkce vrací můj handle, handle tohoto enta.
7.1.7
samemaintype(+hHandle1, +hHandle2)
NOAI
Predikát uspěje, pokud se zadané handly shodují v hlavním typu.
7.1.8
isMember(+hHandle, +sSeznam)
NOAI
určí, zda handl hHandle je členem seznamu sSeznam.
7.1.9
isMember(+mMember, +ssSeznam)
NOAI
určí, zda member mMember je členem superseznamu ssSeznam.
7.1.10
Member(-hHandle, +sSeznam)
NOAI
handlu hHandle přiřadí hodnotu dalšího následujícího handlu ze seznamu sSeznam. Používá se pro repeat cykly.
7.1.11
Member(-mMember, +ssSeznam)
NOAI
memberu mMember přiřadí hodnotu dalšího následujícího memberu ze superseznamu ssSeznam. Používá se pro repeat cykly.
7.1.12
c length(-hHandle, +sSeznam)
NOAI
v parametru hHandle vrací délku seznamu sSeznam.
7.1.13
c length(-hHandle, +ssSeznam)
NOAI
v parametru hHandle vrací délku superseznamu ssSeznam.
7.1.14
c length(-hHandle, +mMmeber)
NOAI
v parametru hHandle vrací délku seznamu "ale ne" memberu mMmember.
7.1.15
ssDomainInitialize(±ssSeznam, +han1, +han2)
NOAI
nastaví v superseznamu všechny hodnoty v sloupečku odpovídajícímu han1 na hodnotu han2.
94
KAPITOLA 7. KNIHOVNA E
7.1.16
c vlastnost(+hPredmet, +hProperty, ±hValue)
NOAI
Tato funkce pro předmět hPredmet a standardní vlastnost hProrerty určí jakou má hodnotu a vrátí ji v položce hValue resp. pouze otestuje, zda má předmět hodnotu hValue dané vlastnosti.
7.1.17
c readVariableFakt(+hSmysl, -sParams)
NOAI
Tato funkce vyhledá v databázi pod smyslem hSmysl první fakt, který najde, a jeho parametry vrátí v seznamu sParams. Výhodou jest, že daný smysl nemusí mít pevný (ale pouze omezený) počet parametrů.
7.1.18
c readelVariableFakt(+hSmysl, -sParams)
NOAI
Tato funkce vyhledá v databázi pod smyslem hSmysl první fakt, který najde, jeho parametry (bez tzv. tajných handlů) vrátí v seznamu sParams a fakt z databáze vymaže. Výhodou jest, že daný smysl nemusí mít pevný (ale pouze omezený) počet parametrů.
7.1.19
decont(+hHandleIn, -hHandleOut)
NOAI
Tato funkce transfomuje handl hHandleIn do handlu hHandleOut. Je-li hHandleIn handl určení místa „v kontejneruÿ nebo „na podložceÿ, pak jej funkce transformuje v handl obyčejného kontejneru či podložky. Jinak v ostatních případech se funkce chová jako identita.
7.1.20
cOdhadVzdalenosti(+sZakazaneDvere, +hStart, +hCil, -hDist) NOAI
Vrací odhad vzdálenosti mezi dlaždicemi v různých místnostech. Odhad vychází z nejkratší cesty místnostmi a zohledňuje v rámci místností pouze geometrickou vzdálenost mezi dveřmi, nikoli skutečnou vzdálenost s ohledem na překážky.
7.1.21
cVejduSeKam(+hDlazdice)
NOAI
Uspěje, pokud se ent vejde na danou dlaždici. Tj. pokud tam nikdo nestojí a je tam ještě dost místa.
7.1.22
Funkce pro určování času
Tato sekce popisuje několik užitečných funkcí pro operace s časem. Tyto funkce vesměs negenerují atomickou instrukci. • now(-hTime)
7.1. VESTAVĚNÉ FUNKCE
95
vrací handl aktuálního času. • stripDay(+hTime, -hNum) vrací v parametru hNum počet kol od začátku dne do času hTime toho dne. • getHour(+hNum, -hHour) Vrací v parametru hHour počet hodin, vypočítaných z počtu kol hNum. • getMin(+hNum, -hMin) vrací v parametru hMin počet minut, vypočítaných z počtu kol hNum. • getDay(-hDay) vrací v parametru hDay počet dní od začátku běhu programu. První den má hodnotu 0.
7.1.23
Funkce pro práci s handly
Tyto funkce provádí různé operace a testy s handly, zejména pro kontejnery a podložky. • c cont(+hPred, -hCont) tato funkce udělá z kontejneru hPred handl hCont, reprezentující stav „v kontejneruÿ. • c podlozka(+hPred, -hPodlozka) tato funkce udělá z podložky hPred handl hPodlozka, reprezentující stav „na podložceÿ. • c isCont(+hPred) tato funkce otestuje, zda předmět hPred je handlem, reprezentujícím stav „v kontejneruÿ. • c isPodlozka(+hPred) tato funkce otestuje, zda předmět hPred je handlem, reprezentujícím stav „na podložceÿ.
96
7.2
KAPITOLA 7. KNIHOVNA E
Knihovna seznamy.e
Tato knihovna obsahuje skripty, které umožňují vykonávat různé operace se seznamy.
7.2.1
c getFirst(-hHandle, +sSeznam)
NOAI
odstraní první prvek ze seznamu sSeznam a vrátí prvek v parametru hHandle a zkrácený seznam v sSeznam.
7.2.2
c getFirst(-mMember, +ssSeznam)
NOAI
odstraní první member ze superseznamu ssSeznam a vrátí ho v parametru mMember a též zkrácený superseznam v ssSeznam.
7.2.3
conc(±sSeznam1, +sSeznam2)
NOAI
spojí dva seznamy sSeznam1 a sSeznam2 a vrátí jejich konkatenaci v parametru sSeznam1.
7.2.4
conc(±ssSeznam1, +ssSeznam2)
NOAI
spojí dva superseznamy ssSeznam1 a ssSeznam2 a vrátí jejich konkatenaci v parametru ssSeznam1.
7.2.5
setridVzdalenost(+hOdkud, ±ssKam)
NOAI
Setřídí superseznam ssKam dle vzdálenosti od hOdkud od nejmenší. Předměty v superseznamu musí být nepřenosné. Pozor, mění doménu "pocet".
7.2.6
redukce(+ssPredmetu, -sPredmetu)
NOAI
Ze superseznamu ssPredmetu okopíruje handly předmětů do seznamu sPredmetu.
7.3
Knihovna db.e
Knihovna obsahuje funkce pro vybavování si konkrétních handlů předmětů, které splňují požadované vlastnosti. Rovněž jsou v této knihovně zařazeny další funkce pro testování vlastností daných předmětů. Žádná z funkcí této knihovny negeneruje atomickou instrukci. Funkce výhradně čerpají z aktuálního obrazu světa v entově databázi.
7.3. KNIHOVNA DB.E
7.3.1
97
Skripty pro konkretizaci
Tyto skripty slouží k vyhledávání předmětů v databázi podle zadaných údajů, jako jsou vlastnosti, určení místa, počtu, skupiny apod. Skripty tohoto typu vždy uspějí, i když nenajdou žádné řešení. Existuje mnoho podobných konkretizačních skriptů, liší se pouze v malé úpravě vstupních či výstupních údajů.
konkretizujPredmetyNekde(+mVzor, -ssNevyh, -ssVysl, +hDruhVypisu) Základní konkretizační skript, který používají ostatní skripty. Tento skript podle vlastností, udaných v parametru mVzor, vyhledá v databázi všechny objekty, které mu odpovídají, a vydá na výstup v supersezanmu ssVysl. Skript vyhledává dle stadardních vlastností, tzn. ve speciálech nesmí být žádné odvozené a výpočtové vlastnosti. Skript navíc vyplní většinu domén v každém členu tohoto superseznamu, a to i výpočtové vlastnosti. Je-li vyhledávaný typ předmětu sebratelný (tj. lze sbírat a pokládat), pak skript výsledný superseznam ssVysl setřídí podle relevance. V parametru ssNevyh skript vrací v doméně "pocet" počet objektů, které se mu z požadovaných nepodařilo konkretizovat. Je-li na vstupu member s doménou "pocet" rovnou volné proměnné či nule, snaží se skript konkretizovat všechny objeky. Parametr hDruhVypisu obsahuje handl HMvypis-dlouhyH nebo HMvypis-kratkyH podle toho, zda se mají na výstupu vyplnit všechny domény, nebo jen domény "predmet" a "relevance". U vstupního memberu mVzor je doména "priorita" ignorována, doména "relevance" se bere za nejnižší míru relevance, kterou musí výstupní hodnoty přesáhnout. Je-li zadána konkrétní hodnota domény "kde" u memberu mVzor, pak musí být zadána i hodnota domény "dlazdice". Je-li zadána hodnota domény "dlazdice" s negací, pak je nutné nastavit i konkrétní hodnotu v doméně "kde". Pro hodnoty vlastností lze použít pro konkretizaci rozsahových konstant, ale na výstupu se vydávají pouze konkrétní hodnoty vlastností. Existují varianty tohoto skiptu, které • místo výstupního superseznamu ssVysl vrací pouze seznam předmětů sVysl (zde se využije krátký výpis) • místo vstupního memberu mVzor mají superseznam ssVzor a vyhledávají postupně dle každého členu tohoto superseznamu • nebo obojí Následuje podrobný popis jednotlivých variant skriptu konkretizujPredmetyNekde():
98
KAPITOLA 7. KNIHOVNA E
konkretizujPredmetyNekde(+mVzor, -ssNevyh, -ssVysl) zavolá skript konkretizujPredmetyNekde(mVzor, ssNevyh, ssVysl, "vypis dlouhy"), tedy automaticky s dlouhým výpisem.
konkretizujPredmetyNekde(+mVzor, -ssNevyh, -sVysl) na výstup v parametru sVysl vydá seznam (a nikoli superseznam) konkretizovaných předmětů.
konkretizujPredmetyNekde(+ssVzor, -ssNevyh, -ssVysl, +hDruhVypisu) konkretizuje ne pro jeden vstupní member, ale postupně pro všechny membery superseznamu ssVzor a výsledné superseznamy (po odstranění duplicit) skonkatenuje do parametru ssVysl.
konkretizujPredmetyNekde(+ssVzor, -ssNevyh, -ssVysl) zavolá skript konkretizujPredmetyNekde(ssVzor, ssNevyh, ssVysl, "vypis dlouhy"), tedy automaticky s dlouhým výpisem.
konkretizujPredmetyNekde(+ssVzor, -ssNevyh, -sVysl) konkretizuje ne pro jeden vstupní member, ale postupně pro všechny membery superseznamu ssVzor a výsledné seznamy předmětů (po odstranění duplicit) skonkatenuje do parametru sVysl. Další skript na konkretizaci je:
konkretizujPredmety(+mVzor, -ssNevyh, -ssVysl) Konkretizuje pouze předměty, které ent vidí. Opět existují varianty, které • místo výstupního superseznamu ssVysl vrací seznam předmětů sVysl • místo vstupního memberu mVzor mají superseznam ssVzor • nebo obojí Podrobný popis těchto variant následuje. Všechny nějakým způsobem využívají skript konkretizujPredmetyNekde(). Nebyl zaveden parametr hDruhVypisu, pro superseznamy se používá automaticky plný výpis:
7.3. KNIHOVNA DB.E
99
konkretizujPredmety(+mVzor, -ssNevyh, -ssVysl) konkretizuje všechny viděné předměty dle vstupního memberu mVzor a naplní jimi superseznam ssVysl, nevyhovující počty nastaví do ssNevyh.
konkretizujPredmety(+mVzor, -ssNevyh, -sVysl) na výstup v parametru sVysl vydá seznam (a nikoli superseznam) konkretizovaných předmětů, které navíc vidí.
konkretizujPredmety(+ssVzor, -ssNevyh, -ssVysl) konkretizuje viděné předměty ne pro jeden vstupní member, ale postupně pro všechny membery superseznamu ssVzor a výsledné superseznamy předmětů (po odstranění duplicit) skonkatenuje do parametru ssVysl.
konkretizujPredmety(+ssVzor, -ssNevyh, -sVysl) konkretizuje viděné předměty ne pro jeden vstupní member, ale postupně pro všechny membery superseznamu ssVzor a výsledné seznamy předmětů (po odstranění duplicit) skonkatenuje do parametru sVysl. Zbývajícími konkretizačními skripty jsou:
konkretizujPredmetyUSebe(+mVzor, -ssNevyh, -ssVysl) Konkretizuje předměty, které má u sebe, tj. v rukou nebo v inventáři. Následující skripty slouží ke zjištění resp. potvrzení hodnot nějaké vlastnosti. Na rozdíl od konkretizačních skriptů tyto uspějí, pouze když naleznou řešení.
7.3.2
vidimPredmety(+hPredmet, -ssVysl)
Vrací v proměnné ssVysl superseznam předmětů typu hPredmet, které vidím.
7.3.3
vidimPredmetVlastnosti(+mPredmet, -hPredmet)
Skript dle memberu mPredmet vyhledá co předmet s největší relevací, který se vrací v proměnné hPredmet. Vstupní hodnota proměnné hPredmet se ignoruje.
7.3.4
odpovida(+hVec, -mVzor)
Skript určí, zda předmět hVec má vlastnosti, uvedené v memberu mVzor.
100
7.3.5
KAPITOLA 7. KNIHOVNA E
stav(+hPredmet,+hVlastnost, ±hHodnota)
Tento skript pro předmět hPredmet určí hodnotu vlastnosti hVlastnost a vrací ji v proměnné hHodnota, popř. otestuje zda předmět má danou hodnotu. Vlastností může být libovolná základní vlastnost nebo doména superseznamu.
7.3.6
stavEnta(+hVlastnost, ±hHodnota)
Určí (resp. potvrdí či vyvrátí při zadané hodnotě), hodnotu hHodnota vlastnosti hVlastnost pro mě jako enta (např. hlad apod.).
7.3.7
vejdeSe(+hCo, +hDoceho)
Určí, zda se předmět hCo vejde do kontejneru hDoceho, přičemž obě proměnné musí být konkrétní.
7.3.8
vidim(+hPredmet)
Určí, zda vidím předmět hPredmet, přičemž proměnná může být konkrétní či libovolně zobecněná. Existují varianty • vidim(+sPredmetu) • vidim(+ssPredmetu) které určí, zda vidím celý seznam resp. superseznam předmětů.
7.3.9
vidimEnta(±hMisto, ±hEnt)
Vrací, zda vidím enta hEnt na pozici hMisto. Obě proměnné mohou být konkrétní nebo volné proměnné. Lze se tedy dotazovat na pozici, na enta či na obojí.
7.3.10
mamUSebe(+hPredmet)
Vrací, zda mám u sebe předmět hPredmet, přičemž proměnná může být konkrétní či libovolně zobecněná.
7.3.11
mamVRuce(+hPredmet)
Vrací, zda mám v ruce předmět hPredmet, přičemž proměnná může být konkrétní či libovolně zobecněná.
101
7.3. KNIHOVNA DB.E
7.3.12
vimO(+hPredmet)
Určí, zda vím o nějakém předmětu typu hPredmet. Existují i varianty • vimO(+sPredmetu) • vimO(+ssPredmetu) které určí, zda vím o celém seznamu resp. superseznamu předmětů.
7.3.13
vimO(+mPredmet,-ssVysl) vimO(+mPredmet,-sVysl)
Určí, zda vím o nějakém předmětu, který odpovídá memberu mPredmet. Pokud ano, vrací výsledný superseznam resp. seznam nalezených předmětů v proměnné ssVysl resp. sVysl.
7.3.14
cont(+hPred, +hKde, -hVysl)
NOAI
Podle parametru hKde ("NA", "DO" či ) transformuje z předmětu hPred handl (a uloží v parametru hVysl) reprezentující stav „na podložceÿ, nebo „v kontejneruÿ nebo pouze obyčejný předmět.
7.3.15
decontLong(+hPred, -hKde, -hVysl)
NOAI
Je-li předmět v parametru hPred typu „na podložceÿ, nebo „v kontejneruÿ nebo pouze obyčejný předmět, pak skript naplní položku hKde na "NA", "DO" či , a předmět hPred transformuje do hVysl na handl obyčejného předmětu.
7.3.16
tatoMistnost(-hMistnost)
NOAI
Vrací handle místnosti, kde se ent právě nachází.
7.3.17
nekdoDrzi(-hVec)
NOAI
Uspěje, pokud předmět někdo drží (v ruce či podle dostupných informací v inventáři), předmět mohu držet i já sám. Pokud je ent v téže místnosti jako já, je informace spolehlivá. Pokud je ent již v jiné místnosti, databáze může obsahovat údaj neaktuální. Nejkomplikovanější situace může nastat, jsem viděl enta, jak předmět držel a jak si ho přesunul do inventáře (tím mi předmět zmizí z očí). Ent pak odejde do jiné místnosti a předmět položí. Až se vrátí, já si budu stále myslet, že má ent předmět ve svém inventáři, a to až do chvíle, než jej uvidím na vlastní oči ležet někde jinde.
102
KAPITOLA 7. KNIHOVNA E
7.3.18
jeTuTaky(±hEnt)
7.3.19
jeTuNekdo(-hEnt)
NOAI
NOAI
Uspěje, pokud je daný ent v téže místnosti jako já. Je-li v argumentu zadána volná proměnná, uspěje, pokud je v místnosti kdokoli jiný než já, a v proměnné vrátí handle tohoto náhodně vybraného enta.
7.3.20
vsechnyMistnosti(-sMistnosti)
NOAI
Vrací seznam všech místností, které jsou v tomto světě zavedeny.
7.4
Knihovna chuze.e
Knihovna chuze.e obsahuje funkce pro snadné přemisťování enta ve světě.
7.4.1
jdi(+hMistnost)
AI
Ent se pokusí dojít do dané místnosti. Hledá cestu domem, hledá cestu místnostmi, otevírá dveře. Výjimky: • nevede-li po tři kola volná cesta ke zvoleným dveřím (někdo entovi překáží ap.), naplánuje se nová cesta do místnosti tak, aby problematické dveře nezahrnovala • nedaří-li se po tři kola projít dveřmi (někdo je na druhé straně ap.), naplánuje se nová cesta do místnosti tak, aby problematické dveře nezahrnovala • nedaří-li se přejít místnost k cílovým dveřím za dobu delší než je dvojnásobek vzdálenosti přes místnost (někdo entovi v každém kole překáží, ovšem v každém kole existuje v místnosti volná cesta ke dveřím), naplánuje se nová cesta do místnosti tak, aby nezahrnovala nedostupné dveře • jakmile jsou nějaké dveře označeny jako problematické, není je možné v rámci tohoto zavolání jdi již použít, i když třeba důvod problémů již pominul • neexistuje-li již žádná cesta místnostmi do zvolené místnosti, skript selže
7.4.2
jdiK(+hVec)
AI
Ent se pokusí dojít k danému předmětu nebo i dlaždici. Umí dojít i do jiné místnosti. Spoléhá na údaj o poloze předmětu tak, jak je aktuálně uložen v databázi. Pokud uvidí předmět někde po cestě, dojde k němu stejně, na nové místo. Pokud po příchodu do cílové místnosti zjistí, že předmět tu není, selže. Předmět nehledá. Pokud bude předmět spatřen u nějakého enta, selže.
103
7.5. KNIHOVNA PREDMETY.E Výjimky: • výjimky pro chození místnostmi viz 7.4.1(str.
102)
• pokud v rámci jedné místnosti po tři kola neexistuje cesta k předmětu, selže • pokud v rámci jedné místnosti trvá cesta k předmětu více než dvojnásobek vzdálenosti přes místnost, selže
7.4.3
usporadanaProchazka(+sMistnosti)
AI
Ent postupně projde uvedené místnosti v daném pořadí. Skript postupně volá funkci jdi. Pokud se do nějaké místnosti nepodaří dojít, selže.
7.4.4
usporadanaProchazka(+sMistnosti, +sNeprosel)
AI
Jednoduchá obměna funkce usporadanaProchazka(sMistnosti). Uspěje vždy, ve druhé proměnné vrátí zbytek seznamu místností, které se již projít nepodařilo. Pokud se podaří projít všechny místnosti, vrací prázdný seznam.
7.4.5
naplanujPohodlnyPruchod(+sMistnosti, -sUsporadane) NOAI
Pro daný seznam místností na základě velikostí místností a metrické vzdálenosti mezi dveřmi hladovým algoritmem uspořádá místnosti tak, aby první z místností byla ta, která je z aktuální místnosti nejbližší. Jako počáteční místnost se vždy bere ta, kde se ent právě nachází.
7.5
Knihovna predmety.e
Knihovna predmety.e obsahuje funkce pro snadné hledání, sbírání a pokládání předmětů.
7.5.1
hledej
AI
Skript hledej je velmi obecný skript na hledání předmětů, které vyhovují daným vlastnostem. Pro zadaný popis hledaných typů předmětů začně procházet místnostmi, dokud má ještě naději, že nějaké předměty daného typu najde. Skript lze použít buď ve variantě hledej, nebo hledejASeber, kdy ent navíc všechny nalezené předměty hned dává do inventáře. Obě varianty přijímají jako argumenty:
104
KAPITOLA 7. KNIHOVNA E
• +ssHledamPredmety, -ssNenasel, -sNasel Skript nakonec vždy uspěje a vrátí seznam konkrétních předmětů, které našel, a seznam obecných předmětů, které nenašel. • +mHledanyPredmet, -hNasel Skript uspěje, jen pokud se podaří najít hledaný předmět. Výjimky: • popis výjimek pro přesun mezi místnostmi viz jdi, strana 7.4.1(str.
102)
• popis výjimek pro chůzi k vhodnému předmětu, jakmile jsem jej spatřil, viz jdiK, strana 102
7.5.2
seber(+hPredmet, +Jak)
AI
Skript sebere daný konkrétní předmět, pokud jej nikdo cizí nedrží. Parametr Jak může být buď "DO" (do inventáře) nebo "NA" (do ruky). Držím-li předmět já sám, skript korektně přesune předmět do ruky či inventáře. Selže, pokud někdo předmět drží nebo pokud předmět nebyl na místě, kde podle údajů v databázi měl být a ani jsme ho nikde cestou neviděli. Pokud chci předmět sebrat do ruky a už mám ruce plné, může rovněž selhat. Aby došel k předmětu používá jdiK (viz stranu 102 pro popis výjimečných situací).
7.5.3
poloz(+hPredmet, +hKontejner, +Jak)
AI
Skript položí daný předmět na určené místo. Argument Jak může být buď "DO" (do kontejneru) nebo "NA" (na podložku či na dlaždici). Skript selže, pokud předmět nemám u sebe, nebo pokud se nepodaří dojít k určenému místu. Pro popis výjimek viz funkci jdiK, strana 102, která se používá pro příchod k určenému místu.
7.5.4
zpristupniVec(+hVec)
AI
Skript předpokládá, že ent již došel k věci, tj. že je u kontejneru, který věc obsahuje ap. Skript pozotvírá všechny vnořené kontejnery tak, aby s věcí bylo možné manipulovat. Skript může selhat, pokud se ukáže, že věc v kontejneru, kde měla být není. Skript si nezajišťuje stálou přítomnost u věci. Pokud se během otevírání ent dozví, že věc je jinde, nebo pokud ho něco vyruší a ent odejde jinam, skript selže.
105
7.6. KNIHOVNA ENTI.E
7.5.5
otevriKontejner(+hKontejner)
AI
Skript předpokládá, že ent je u kontejneru, který má otevřít. Skript zpřístupní kontejner voláním zpristupniVec a pak jej otevře.
7.5.6
hasMoved(+hPredmet)
NOAI
Uspěje, pokud daný předmět od posledního volání této rutiny změnil své umístění. Předmět, který ještě nebyl nikdy touto funkcí sledován, se při prvním sledování pouze zkontroluje, a neoznamuje se, jako že se posunul.
7.5.7
haveMoved(+sPredmety, -sPosunute)
NOAI
Vrací z daného seznamu předmětů jen ty, které se od posledního volání tohoto testu posunuly (změnily svoji pozici, třeba proto, že je někdo zvednul). Předmět, který ještě nebyl nikdy touto funkcí sledován, se při prvním sledování pouze zkontroluje, a neoznamuje se, jako že se posunul.
7.5.8
vyrobPredmet(+hDruh)
AI
Skript vyhledá nějaký stroj a pokusí se v něm vzrobit daný druh předmětu.
7.5.9
uvolniRuceNaPodlahu
AI
Skript položí vše, co má ent v rukou, někam na podlahu.
7.5.10
uvolniRuceDoInventare
AI
Skript se pokusí vše, co má ent v rukou, vložit do inventáře. Pokud se to nepodaří, odloží předmětz někam na podlahu.
7.5.11
zbavSeVseho
AI
Skript všechny předměty, které ent vlastní, roznosí po světě. Ent bude chodit po místnostech a odkládat předměty po pěti.
7.6
Knihovna enti.e
V této knihovně naleznete funkce pro jednoduchou kooperaci s ostatními enty.
106
7.6.1
KAPITOLA 7. KNIHOVNA E
pockejAzPrijde(±hEnt, [+Limit])
AI
Ent bude provádět aNop, dokud nebude v téže místnosti zadaný ent. Pokud byla v argumentu předána volná proměnná, čeká na příchod libovolného enta a handle příchozího vrací na výstup. Je-li zadán nepovinný argument Limit, čeká nejvýše daný počet kol. Pokud do té doby nikdo nepřijde, selže. Jinak uspěje.
7.6.2
jdiKEntovi(+hEnt, +bAzKNemu)
AI
Chodí po domě a snaží se najít hledaného enta. Argument bAzKNemu může být "yes" nebo "no", podle toho, zda mám dojít až těsně k entovi. Skript selže, pokud se daného enta nepodařilo najít. Pokud ent stále utíká, skript se ho stále pokouší dohnat. Pokud se kolega někam zavře na delší dobu, skript selže. Chcete-li dosáhnout chování, kdy skript enta vyhledá, zkusí k němu dojít, a selže, jakmile kolega opustí místnost, použijte posloupnost volání: jdiKEntovi(hEnt, "no"), // vyhledej enta dojdiKEntoviVTetoMistnosti(hEnt) // dojdi k~němu a selži, uteče-li
7.6.3
dojdiKEntoviVTetoMistnosti(+hEnt)
AI
Předpokládá, že kolega je v této místnosti a dojde k němu. Selže, pokud kolega mezitím místnost opustí.
7.7 7.7.1
Knihovna lingvistika.e Předpoklady pro použití modulu
Modul lingvistika.e obsahuje mj. funkci pro reagování na příkazy (viz str. 107). Tato funkce ovšem spoléhá na to, že uživatel sám připraví predikát provedCiRozkaz(+hKdo, +hRozkaz, +sParametry) implementující reakce na konkrétní příkazy. Použijete-li tedy modul lingvistika.e, musíte definovat predikát provedCiRozkaz alespoň s prázdným tělem, nebo vhodněji tak, aby ent plnění rozkazu odmítl: provedCiRozkaz(hKdo, hRozkaz, sParametry) $- return 0. :-aRekniKomuVetu(hKdo, "neplnim rozkazy"). Pokud tak neučiníte, nebude možné váš skript zkompilovat.
7.7. KNIHOVNA LINGVISTIKA.E
7.7.2
107
vyslovujOdpovedi(+Prio, -ID)
Jak je podrobně popsáno v programátorské dokumentaci, lingvistický modul enta zajišťující pochopení a vyslovování vět není oprávněn přímo odesílat odpovědi na zaslechnuté otázky. Modul tedy odpověď pouze připraví a uloží do databáze, skutečné odeslání však musí zajistit patřičný skript. Potřebný skript je v modulu lingvistika.e zcela připraven ve formě globálního interruptu. Chcete-li tento interrupt používat a umožnit tak entovi vyslovovat připravené odpovědi, stačí jej krátce po spuštění enta navěsit zavoláním predikátu vyslovujOdpovedi(+Prio, -ID). Predikát vyslovujOdpovedi(+Prio, -ID) vždy uspěje a bezprotředně nevykoná žádnou atomickou instrukci. Přitom parametr Prio určuje prioritu, s jakou je globální interrupt navěšen. Tato priorita by měla být co nejvyšší, jinak může poměrně často docházet k překrytá tohoto interruptu jinými interrupty a ent bude působit mlčenlivě, přestože si v duchu odpovědi připravovat bude. V parametru ID pak predikát vrací identifikátor navěšeného interruptu, pokud by jej chtěl uživatel někdy v budoucnu zrušit či zablokovat. Pokud někdy později bude mít ent „na jazykuÿ větu k vyslovení, interrupt se spustí a odešle připravenou větu pomocí jedné atomické instrukce. V případě, že v okamžiku spuštění interruptu nebude adresát odpovědi v téže místnosti jako ent, atomická instrukce selže, ale interrupt toto selhání skryje a tiše uspěje. Ent tedy adresáta zásadně nehledá. Upozornění Pokud nepovolíte vyslovování připravených vět uvedeným postupem, ent nebude ani schopen oznamovat chybová hlášení a vést konkretizační dialog, tj. nebude schopen zeptat se na podrobnosti o předmětu, který nedokázal v příkazu bezpečně identifikovat. Doporučujeme proto navěšovat tento interrupt vždy.
7.7.3
plnRozkazy(+Prio, -ID)
Jakmile entovi někdo řekne nějakou větu, lingvistický modul se zaslechnutou větu pokusí zpracovat. V případě, že se větu podaří zcela pochopit a že se jedná o rozkaz nebo o větu z uživatelské konfigurace (viz kapitolu 5.9(str. 52) ), lingvistický modul zapíše zaslechnutý rozkaz do databáze. Je úkolem rozvrhovače, aby si zaslechnutého rozkazu všiml a provedl odpovídající skript. Pro jednoduchost je v modulu lingvistika.e připraven globální interrupt, který zajistí okamžité provádění reakce na příkaz. (Není samozřejmě vždy žádoucí všechny příkazy ostatních entů bezprostředně vykonávat, pro jednoduchost si však může ent dovolit být poslušný.) Globální interrupt pro plnění rozkazů navěsíte voláním predikátu plnRozkazy(+Prio, -ID) krátce po startu enta. Přitom parametr Prio určuje prioritu, s jakou je globální interrupt navěšen, a v parametru ID predikát vrací identifikátor navěšeného interruptu, pokud byste jej chtěli někdy v budoucnu zrušit či zablokovat. Globální interrupt plnění rozkazů zajišťuje nutnou režii, především však při
108
KAPITOLA 7. KNIHOVNA E
zaslechnutí rozkazu spustí skript provedCiRozkaz(+hKdo, +hRozkaz, +sParametry), který jako autor světa a skriptů musíte dodat. V rámci tohoto skriptu definujete reakce na konkrétní rozkazy, např.: // Tento predikat bude zavolan globalnim interruptem, kdyz nam da nekdo rozkaz provedCiRozkaz(hKdo, hRozkaz, sParametry) $- return 0. :-{ { hRozkaz === "ahoj", print("Pozdravim ", hKdo), pozdrav(hKdo) } OR { hRozkaz === "seber co", c_getFirst(hCo, sParametry), seber(hCo) } OR { hRozkaz === "odloz co", Member(hCo, sParametry), if mamUSebe(hCo) then polozNekamNaPodlahu(hCo) else aRekniKomuVetu(hKdo, "nemam co", hCo) fi } OR { aRekniKomuVetu(hKdo, "nedokazu splnit rozkaz") } }. Proměnná hRozkaz obsahuje identifikátor rozkazu, odpovídá identifikátoru který je uveden v konfiguračním souboru vět. Podobně seznam sParametry obsahuje argumenty tak, jak byly ze vstupní věty identifikovány, jejich pořadí a význam parametrů je určen definicí věty v konfiguračním souboru vět. Popis konfiguračního souboru vět naleznete na straně 52. V ukázce stojí též za pozornost, že poslední záchytná varianta větvení, kdy ent oznamuje nesplnění rozkazu, se použije jednak v případě, že byl entovi dán jiný rozkaz, než které podmínka pokrývá, a jednak v případě, že rozkaz správně identifikován byl, ale jeho vykonávání selhalo. K tomu v naší ukázce dojde například, selže-li skript na sebrání předmětu. Záchytná větev ovšem neověřuje, zda se původce rozkazu nachází v téže místnosti jako já, chybovou hlášku se tak poměrně často nepodaří vyslovit. Upozornění
7.8. KNIHOVNA ROZVRHOVAC.E
109
V tomto jednoduchém schématu vykonávání zadaných příkazů dochází k problému s prioritou interruptů. Dostane-li ent příkaz a v průběhu jeho vykonávání ještě příkaz další, handler druhého příkazu (i když je příkaz dobře pochopen) přijde do hry bezprostředně po dokončení příkazu prvního, ale nikoli v jeho průběhu. Lidštějšího chování by bylo možné dosáhout jedině složitou kooperací lingvistického interruptu s 7.8(str. 109) .
7.7.4
aRekniKomuVetu(+hAdresat, +hVeta, ...)
Atomická instrukce aRekniKomuVetu není sice součástí modulu lingvistika.e a je vestavěna v interpretu jazyka E, tematicky je však vhodné dokumentovat ji právě zde. Parametr hAdresat určuje, kterému entovi má být výrok směrován. Parametr hVeta určuje typ věty, kterou má ent vyslovit. Identifikátor věty se zadává ve formě konstanty, věta téže identifikující konstanty musí být definována v konfiguračním souboru vět (viz str. 52). Za typem věty mohou následovat další parametry, jejich pořadí i typy musí přesně odpovídat definici věty z konfiguračního souboru. Porušení konzistence bude mít za důsledek chybu za běhu enta, nikoli v době kompilace skriptu. Pokud adresát sdělení není v daném okamžiku ve stejné místnosti jako mluvčí, instrukce neselže.
7.8
Knihovna rozvrhovac.e
Knihovna rozvrhovac.e obsahuje implementaci jednoduchého rozvrhovače úloh. K dispozici je sada několika funkcí, jimiž lze rozvrhovač ovládat.
7.8.1
Princip činnosti rozvrhovače
Rozvrhovač pracuje s úlohami. Úlohami rozumíme všechny celistvé činnosti, které má ent během svého života provádět (např. zalévání, okopávání, pití apod. – nikoli však již třeba chůze do zahrady v rámci zalévání). Úkolem rozvrhovače je řídit, kterou úlohu má ent v daném okamžiku provádět. Množina úloh, nad níž rozvrhovač operuje, není neměnná – lze do ní úlohy přidávat a stejně tak lze z ní úlohy odebírat. Rozvrhování probíhá v rámci jednoho dne, žádná z úloh nemůže „přejítÿ do nového dne; rozvrhovač na konci dne všechny úlohy ukončí a ráno je „nastartujeÿ znovu. Rozhodování o tom, která úloha má být v daném okamžiku prováděna, je realizováno na základě priorit úloh. Prováděna je ta úloha, která má aktuálně nejvyšší prioritu. Úloha může mít v zásadě dva typy priorit: konstantní a lichoběžníkovou. Konstantní prioritu mají ty úlohy, jejichž priorita se v rámci
110
KAPITOLA 7. KNIHOVNA E
dne nemění. Lichoběžníková priorita je dynamická a je charkterizována lichoběžníkem (viz obrázek 7.1(str. 110) ). Tento lichoběžník je popsán svým počátkem (start), výškou (výška), bodem zlomu (zlom) a svým koncem (konec). Aktuální priorita úlohy je dána výškou lichoběžníku v daném čase. Priorita úlohy před časem start a po čase konec je nulová. Rozvrhovač automaticky aktivuje lingvistickou podporu. priorita 6 výška
@
start
@
@
zlom
@
@
konec
-
čas
Obrázek 7.1: Popis priority úlohy pomocí lichoběžníku Úlohy rozvrhovač dělí do dvou základních skupin: na obyčejné úlohy a na životní funkce. Obyčejné úlohy jsou zadány pouze předpisem (skriptem) pro vykonání úlohy a svojí prioritou. Životní funkce navíc obsahují ještě předpis pro podmínku, kdy se mají aktivovat. Pokud není podmínka splněna, životní funkce se neaktivuje. Příkladem životní funkce může být pití, které se aktivuje tehdy, když žízeň enta překročí jistou stanovenou mez. Obyčejné úlohy mohou být jendorázové (provedou se jen jednou) nebo pravidelné (budou se provádět každý den). Každá úloha je v rámci rozvrhovače jednoznačně identifikována tzv. identifikátorem úlohy (UID).
7.8.2
Předpoklady pro použití rozvrhovače
Aby mohl rozvrhovač korektně pracovat, je nutné zajistit, aby jako první z funkcí pro práci s rozvrhovačem byla volána funkce rozvrhovacInit. Tato funkce provede počáteční inicializaci rozvrhovače. Poté je již možné rozvrhovači specifikovat jednotlivé úlohy a životní funkce. Volání RozvrhovacStart spustí rozvrhovač. Rozvrhovač mimo jiné aktivuje i lingvistickou podporu. K tomuto účelu je nutné, uživatel definoval predikát plnRozkazy (blíže viz kapitola 7.7.3 (str. 107) ). Není nutné explicitně používat modul lingvistika.e, protože rozvrhovač lingvistickou podporu inicializuje za pomoci vlastních prostředků. Příklad použití rozvrhovače: Zij $return 0.
7.8. KNIHOVNA ROZVRHOVAC.E
111
:rozvrhovacInit, rozvrhovacRegistrujUlohu("muj_skript", _, 100, 35, 120, 140, uid), rozvrhovacRegistrujZivotniFunkci("odskocSi", "odskocSiTest", 70, _), rozvrhovacRegistrujZivotniFunkci("osprchujSe", "osprchujSeTest", 55, _), rozvrhovacRegistrujZivotniFunkci("umyjSiRuce", "umyjSiRuceTest", 50, _), rozvrhovacRegistrujZivotniFunkci("zazenZizen", "zazenZizenTest", 80, _), rozvrhovacRegistrujZivotniFunkci("jdiSpat", "jdiSpatTest", 85, _), rozvrhovacStart.
7.8.3
rozvrhovacInit
Inicializace rozvrhovace. Tuto funkci je nutné volat jako první.
7.8.4
rozvrhovacStart
Spuštění rozvrhovače. Po spuštění rozvrhovače lze přidávat či odebírat úlohy.
7.8.5
rozvrhovacRegistrujUlohu(+uloha, +hParametr, +lStart, +lVyska, +lZlom, +lKonec, -UID)
Registrace pravidelné úlohy s lichoběžníkovou prioritou. Hodnoty popisující lichoběžník jsou absolutní počty kol od začátku dne. Úloha se stane každodenní entovou úlohou. Úloze bude předán parametr hParam. Funkce vrací UID úlohy. Registrace úlohy během dne se projeví až ve dni následujícím.
7.8.6
rozvrhovacRegistrujUlohu(+uloha, +hParametr, +priorita, -UID)
Registrace pravidelné úlohy s konstantní prioritou. Úloha se stane každodenní entovou úlohou. Úloze bude předán parametr hParam. Funkce vrací UID úlohy. Registrace úlohy během dne se projeví až ve dni následujícím.
7.8.7
rozvrhovacSpustUlohu(+uloha, +hParametr, +lStart, +lVyska, +lZlom, +lKonec, -UID)
Spuštění jednorázové úlohy s lichoběžníkovou prioritou. Hodnoty popisující lichoběžník jsou absolutní počty kol od začátku dne. Úloze bude předán parametr hParam. Funkce vrací UID úlohy.
112
7.8.8
KAPITOLA 7. KNIHOVNA E
rozvrhovacSpustUlohu(+uloha, +hParametr, +priorita, -UID)
Spuštění jednorázové úlohy s konstantní prioritou. Úloze bude předán parametr hParam. Funkce vrací UID úlohy.
7.8.9
rozvrhovacRegistrujZivotniFunkci(+akce, +podminka, +lStart, +lVyska, +lZlom, +lKonec, -UID)
Registrace životní funkce s lichoběžníkovou prioritou. Hodnoty popisující lichoběžník jsou absolutní počty kol od začátku dne. Úloha se stane entovou životní funkcí. Životní funkce je aktivována po spuštění podmínky. Funkce vrací UID životní funkce. Registrace během dne se projeví až dne následujícího.
7.8.10
rozvrhovacRegistrujZivotniFunkci(+akce, +podminka, +priorita, -UID)
Registrace životní funkce s konstantní prioritou. Úloha se stane entovou životní funkcí. Životní funkce je aktivována po spuštění podmínky. Funkce vrací UID životní funkce. Registrace během dne se projeví až dne následujícího.
7.8.11
rozvrhovacOdeberUlohu(+UID)
Odebrání pravidelné úlohy s daným UID. Neuspěje, pokud úloha neexistuje. Lze použít i pro životní funkce. Úloha již nikdy nebude naplánována, její vykonávání se ukončí
7.8.12
rozvrhovacNazevUlohy(+UID, -uloha)
Získání názvu úlohy s daným UID. Název odpovídá jménu skriptu. Lze použít i pro životní funkce. Funkce vrací volnou proměnnou, pokud úloha neexistuje
7.8.13
rozvrhovacAktualniUloha(-UID)
Získání UID úlohy, kterou ent právě provádí. Funkce vrací volnou proměnnou, pokud ent nic neprovádí.
7.9. KNIHOVNA ZIVOTNIFUNKCE.E
7.9
113
Knihovna zivotnifunkce.e
Tato knihovna poskytuje funkce pro ošetření základních životních potřeb entů. Tyto ošetřovací funkce lze nejlépe využít v součinnosti s rozvrhovačem (viz kapitola 7.8(str. 109) ). Ošetřovací funkce jsou implementovány jako dvojice: test a akce. V případě, že uspěje test, aktivuje se odpovídající akce. Testem může být například ověření hladu enta a akcí vyhledání a snědení jídla.
7.9.1
ZFodskocSiTest
Test, zda se entovi chce na záchod.
7.9.2
ZFodskocSi
Ent si odskočí na záchod.
7.9.3
ZFosprchujSeTest
Test, zda je ent tak špinavý, že se bude muset osprchovat.
7.9.4
ZFosprchujSe
End se zajde osprchovat.
7.9.5
ZFumyjSiRuceTest
Test, zda má ent špinavé ruce.
7.9.6
ZFumyjSiRuce
Ent si dojde umýt ruce.
7.9.7
ZFnapijSeTest
Test, zda má ent žízeň.
7.9.8
ZFnapijSe
Ent se někde napije.
114
7.9.9
KAPITOLA 7. KNIHOVNA E
ZFnajezSeTest
Test, zda má ent hlad.
7.9.10
ZFnajezSe
Ent vyhledá jídlo a nají se.
7.9.11
ZFjdiSpatTest
Test, zda je ent tak unavený, že si musí lehnout.
7.9.12
ZFjdiSpat
Ent si lehne do postele a usne.
7.9.13
ZFosetriSeTest
Test, zda je ent zraněný.
7.9.14
ZFosetriSe
Ent se ošetří sprejem.
7.10
Knihovna debugging.e
V této knihovně naleznete funkce pro pohodlné navození konkrétních situací ve světě entů, což je potřeba zvláště při ladění skriptů.
7.10.1
jdiNaPeska(+mPopisPeska, -hPesek)
AI
Počká, až předmět daného popisu v místnosti změní polohu. (Ihned selže, pokud v místnosti není žádný odpovídající předmět.) Jakmile k tomu dojde, trvale sleduje předmět, snaží se k němu dojít. Až předmět opět změní polohu, dojde přesně na dlaždici, kde předmět leží, uspěje a vrátí handle předmětu. Skript se užívá zvláště v případě, že chceme enta provést některými místnostmi. Začneme tím, že v místnosti vezmeme sledovaný typ předmětu lidským entem do ruky. Strojový ent pak začne konkrétní předmět sledovat, tj. sleduje i nás. Můžeme projít potřebnými místnostmi a v cílové místnosti položit předmět na podlahu. Ent dojde přesně na pozici předmětu (a pokračuje dále v připraveném skriptu pro ladění).
7.10. KNIHOVNA DEBUGGING.E
115
Během vedení enta světem je třeba vždy počkat, až ent spatří, kterými dveřmi odcházíme z místnosti. Nesmíme entovi utéct.
116
KAPITOLA 7. KNIHOVNA E
Kapitola 8
Debugger skriptů 8.1
Stručný popis
Debugger je jednoduchou aplikací, jejímž hlavním úkolem je zjednodušit a urychlit ladění skriptů v jazyce E. Za normálních okolností se jedná o zdlouhavou a nepříliš transparentní činnost, kterou však existence debuggeru výrazně usnadňuje. Debugger disponuje přehledným okenním uživatelským rozhraním, jehož prostřednictvím je uživateli umožněno provádět základní úkony potřebné pro ladění skriptů (krokování, spouštění a pozastavování skriptů, nastavovaní breakpointů a podobně). Aplikace je „šita na míruÿ jazyku E a potřebám projektu Enti. Z toho vyplývají některá specifika, která významně ovlivnila výslednou podobu programu. Následující text pokrývá všechny oblasti, které jsou pro práci s programem podstatné.
8.2
Spuštění debuggeru
V zásadě existuje jediná cesta, jak debugger spustit. Při spouštění enta je nutné uvést parametr debug, tedy například: ent debug localhost 24444 Tento příkaz spustí enta, který se pokusí připojit k serveru prostředí běžícímu na lokálním počítači na portu 24444. Pokud připojení uspěje a ent obdrží informaci o umístění svých skriptů, interpret provede syntaktickou kontrolu těchto skriptů. V případě, že je vše v pořádku, interpret spustí debugger; v případě chyby je činnost enta ukončena. 117
118
8.3
KAPITOLA 8. DEBUGGER SKRIPTŮ
Ovládání programu
Debugger po svém spuštění zobrazí hlavní okno (viz obrázek 8.1(str. 118) ). V tomto okně jsou soustředěny všechny prvky, které jsou nutné k práci s programem.
Obrázek 8.1: Hlavní okno debuggeru Hlavní okno je rozčleněno na dvě základní části: v levé polovině okna se nacházejí výpisy zdrojových souborů, v pravé polovině pak dvě záložky pro sledování stavu skriptů. Debugger dále disponuje menu a panelem nástrojů, který víceméně kopíruje jednotlivé položky menu.
8.3.1
Interakce se zdrojovými soubory
V levé části aplikačního okna jsou zobrazovány výpisy zdrojových souborů. Soubory jsou organizovány do záložek, každému souboru odpovídá samostatná záložka (nadepsaná jménem souboru). Po spuštění programu je otevřen jediný soubor – ten, v němž je činnost skriptu zahájena. V průběhu vykonávání skriptu mohou být volány funkce i z jiných (includovaných) souborů. Tehdy debugger tyto soubory automaticky otevírá. Zdrojové texty jsou vypisovány po řádkách; každá řádka je pro zvýšení čitelnosti očíslována. Při zobrazování zdrojových textů jsou navíc rozlišovány čtyři základní typy řádek: • aktivní řádka je ta řádka skriptu, kterou se interpret právě chystá vykonat. Aktivní řádka existuje v každém okamžiku právě jedna a je zvýrazněna
8.3. OVLÁDÁNÍ PROGRAMU
119
modrou barvou. Informaci o aktivní řádce poskytuje též text pod výpisy souborů, který informuje o aktuálním souboru a čísle vykonávané řádky • breakpoint je taková řádka, na níž se má vykonávání skriptu zastavit. Breakpointy jsou zvýrazněny červenou barvou • zarážka může existovat nejvýše jedna a indikuje řádku, k níž se má skript vykonávat. Vytvoření zarážky automaticky způsobí spuštění skriptu. Ten poběží tak dlouho, dokud nenarazí na zarážku nebo na breakpoint (pokud nejsou breakpointy ignorovány – blíže viz kapitola 8.3.3(str. 119) ) nebo dokud není manuálně přerušen. Ať už skript zastaví jakýmkoli způsobem, zarážka zmizí. Zarážky nelze vkládat za běhu skriptu. Řádka, která je zarážkou, je zvýrazněna zelenou barvou • obyčejné řádky jsou všechny ostatní řádky S výpisy zdrojových souborů lze interagovat myší následujícím způsobem: • Stisk nad řádkou skriptu způsobí vložení nebo odstranění již existujícího breakpointu. Seznam breakpointů (viz kapitola 8.3.2(str. 119) ) je příslušně aktualizován. Breakpointy nelze vkládat či rušit za běhu skriptu • Stisk označí řádku pod kurzorem myši jako zarážku. Skript se rozběhne a pokud narazí na danou řádku nebo na breakpoint (pokud nejsou breakpointy ignorovány – viz kapitola 8.3.3(str. 119) ) nebo pokud je manuálně přerušen, je jeho vykonáván zastaveno. Zarážky nelze vkládat za běhu skriptu
8.3.2
Sledování stavu skriptu
V průběhu života skriptu lze v debuggeru sledovat stav skriptu. K tomuto účelu jsou v pravé polovině aplikačního okna zřízeny dvě záložky: záložka Proměnné a záložka Výstup. Záložka Proměnné obsahuje výpis globálních a lokálních proměnných. Proměnné jsou vypisovány ve formátu: jméno proměnné - hodnota. Dále se na záložce nachází seznam existujících breakpointů. Jednotlivé položky tohoto seznamu jsou zobrazeny ve formátu: jméno souboru - řádka. Se seznamem breakpointů lze interagovat myší – kdykoli je nad nějakou z položek stisknuto , v levé části okna je automaticky zobrazen daný soubor na odpovídající řádce. Záložka Výstup je – jak už její název vypovídá – věnována výstupu skriptu. Veškerý textový výstup, který skript vyprodukoval, je zobrazen na této záložce.
8.3.3
Popis menu a panelu nástrojů
Debugger disponuje jednoduchým menu a panelem nástrojů, s jejichž pomocí lze řídit chod programu. Panel nástrojů obsahuje tlačítka, která pouze odkazují na některé z funkčností uvedených v menu. Následující text se proto primárně věnuje popisu jednotlivých položek menu; pokud pro právě probíranou položku existuje její ekvivalent na panelu nástrojů, bude v textu zmíněn.
120
KAPITOLA 8. DEBUGGER SKRIPTŮ
• Menu Soubor obsahuje položky pro manuální otevření zdrojového souboru a pro ukončení programu . Otevřít soubor (též Ctrl-O nebo první tlačítko panelu nástrojů) umožňuje manuální otevření zdrojové souboru. Program při běhu programu otevírá potřebné zdrojové soubory automaticky, nicméně v některých případech může být nutné mít soubor „připravenýÿ s předstihem (například kvůli vložení breakpointu) . Konec (též Ctrl-K ) umožňuje ukončit debugger. Program zašle interpretu skriptů žádost o ukončení ladění a vyčká příchodu potvrzení. Dokud toto potvrzení nedorazí, program se neukončí. Interpret potvrzení většinou zašle obratem, nicméně v některých situacích může dojít k určitým prodlevám. Typicky se jedná o situace, kdy interpret provádí nějakou časově náročnou operaci nebo je zablokován na atomické instrukci1 • Menu Povely obsahuje položky pro řízení běhu skriptu: . Spustit (též F5 nebo druhé tlačítko panelu nástrojů) spustí skript. Během vykonávání skriptu nelze vkládat ani odebírat breakpointy a zarážky. Nejsou aktualizovány seznamy globálních a lokálních proměných, pouze textový výstup skriptu. Běh skriptu lze kdykoli přerušit zvolením položky Pozastavit (viz níže). Pokud skript při svém běhu narazí na breakpoint (a pokud breakpointy nemají být ignorovány – viz položka Ignorovat breakpointy níže), vykonávání skriptu je pozastaveno a je zobrazen dialog informující o výskytu breakpointu (viz obrázek 8.2(str. 120) ). Za běhu skriptu není položka Spustit aktivní
Obrázek 8.2: Dialog s informací o breakpointu . Pozastavit (též Esc nebo čtvrté tlačítko panelu nástrojů) umožňuje pozastavit běh skriptu. Po zastavení debugger zobrazí ten zdrojový soubor, v němž běh skončil, spolu se zvýrazněnou řádkou, kterou se interpret právě chystá vykonat. Pokud skript neběží, není tato položka aktivní . Krokovat (též F4 nebo třetí tlačítko panelu nástrojů) umožňuje krokovat skript příkaz po příkazu. Každá aktivace této položky odpovídá jednomu kroku interpretu. Krokování se zanořuje do podskriptů (nicméně zanořování lze obejít, například používáním zarážek). Po každém kroku 1 O úspěchu či neúspěchu atomické instrukce se ent/interpret dozví teprve tehdy, zpracovalli server prostředí v daném kole instrukce od všech entů – pokud ji některý z entů nedodal, všichni ostatní musí čekat.
8.3. OVLÁDÁNÍ PROGRAMU
121
debugger zobrazí ten soubor, v němž se interpret právě nachází, spolu se zvýrazněnou aktuální řádkou. Pokud skript běží, nelze krokování provádět . Ignorovat breakpointy (též poslední tlačítko panelu nástrojů) dovoluje určit, zda debugger při běhu skriptů zastavuje na breakpointech, nebo je ignoruje. Implicitně nejsou breakpointy ignorovány, avšak zatrhnutím této položky lze toto chování změnit. Položka není přístupná během běhu skriptu . Vstupovat do podmínek interruptů umožňuje specifikovat, zda debugger bude při krokování či běhu skriptu vstupovat také do podmínek interruptů. Program implicitně do podmínek vstupuje, nicméně v některých situacích může být toto chování matoucí, protože interrupty jsou ve své podstatě „jinými vláknyÿ, jejichž podmínky jsou vyhodnocovány po každé atomické instrukci. Položka není přístupná během vykonávání skriptu . Kde jsem? (též F2 ) je užitečná položka, která umožňuje okamžité zorientování ve zdrojových souborech. Zejména je-li otevřeno mnoho souborů, jedná se o nejrychlejší cestu, jak zjistit, v jakém souboru a na jaké řádce se interpret právě nachází • Menu Nápověda obsahuje položky pro zobrazení nápovědy a informací o programu . Nápověda (též F1 ) zobrazí okno s on-line nápovědou k programu . O programu způsobí zobrazení dialogu s informacemi o programu
122
KAPITOLA 8. DEBUGGER SKRIPTŮ
Příloha A
Přehled předmětů Balíček – handle: HObalicekH, konstanta pro použití ve skriptech: "balicek" Barvy – handle: HObarvyH, konstanta pro použití ve skriptech: "barvy" Brambory – handle: HObrambora-rH, konstanta pro použití ve skriptech: "brambory" Brašna – handle: HObrasnaH, konstanta pro použití ve skriptech: "brasna" Brokolice – handle: HObrokolice-rH, konstanta pro použití ve skriptech: "brokolice" Budík – handle: HObudikH, konstanta pro použití ve skriptech: "budik" Celer – handle: HOceler-rH, konstanta pro použití ve skriptech: "celer" Chléb – handle: HOchlebH, konstanta pro použití ve skriptech: "chleb" Dveře – handle: HOdvereH, konstanta pro použití ve skriptech: "dvere" Dřez – handle: HOdrezH, konstanta pro použití ve skriptech: "drez" Flétna – handle: HOfletnaH, konstanta pro použití ve skriptech: "fletna" Generátor – handle: HOgeneratorH, konstanta pro použití ve skriptech: "generator" Hadr – handle: HOhadrH, konstanta pro použití ve skriptech: "hadr" Hadřík – handle: HOhadrikH, konstanta pro použití ve skriptech: "hadrik" Harmonika – handle: HOharmonikaH, konstanta pro použití ve skriptech: "harmonika" Hodiny – handle: HOhodinyH, konstanta pro použití ve skriptech: "hodiny" Housle – handle: HOhousleH, konstanta pro použití ve skriptech: "housle" Hrnec – handle: HOhrnecH, konstanta pro použití ve skriptech: "hrnec" Hrneček – handle: HOhrnekH, konstanta pro použití ve skriptech: "hrnecek" Hřebík – handle: HOhrebikH, konstanta pro použití ve skriptech: "hrebik" 123
124
PŘÍLOHA A. PŘEHLED PŘEDMĚTŮ
Kbelík – handle: HOkyblH, konstanta pro použití ve skriptech: "kbelik" Kladivo – handle: HOkladivoH, konstanta pro použití ve skriptech: "kladivo" Kleště – handle: HOklesteH, konstanta pro použití ve skriptech: "kleste" Kniha – handle: HOknihaH, konstanta pro použití ve skriptech: "kniha" Kolík – handle: HOkolikH, konstanta pro použití ve skriptech: "kolik" Konev – handle: HOkonevH, konstanta pro použití ve skriptech: "konev" Koš – handle: HOkosH, konstanta pro použití ve skriptech: "kos" Květák – handle: HOkvetak-rH, konstanta pro použití ve skriptech: "kvetak" Křeslo – handle: HOkresloH, konstanta pro použití ve skriptech: "kreslo" Lednice – handle: HOledniceH, konstanta pro použití ve skriptech: "lednice" Likvidátor – handle: HOlikvidatorH, konstanta pro použití ve skriptech: "likvidator" Lustr – handle: HOlustrH, konstanta pro použití ve skriptech: "lustr" Lžíce – handle: HOlziceH, konstanta pro použití ve skriptech: "lzice" Mapa – handle: HOmapaH, konstanta pro použití ve skriptech: "mapa" Meč – handle: HOmecH, konstanta pro použití ve skriptech: "mec" Mixér – handle: HOmixerH, konstanta pro použití ve skriptech: "mixer" Motyka – handle: HOmotyckaH, konstanta pro použití ve skriptech: "motyka" Mouka – handle: HOmoukaH, konstanta pro použití ve skriptech: "mouka" Mrkev – handle: HOmrkev-rH, konstanta pro použití ve skriptech: "mrkev" Naběračka – handle: HOnaberackaH, konstanta pro použití ve skriptech: "naberacka" Nůše – handle: HOnuseH, konstanta pro použití ve skriptech: "nuse" Nůž – handle: HOnuzH, konstanta pro použití ve skriptech: "nuz" Obraz – handle: HOobrazH, konstanta pro použití ve skriptech: "obraz" Oprašovadlo – handle: HOoprasovadloH, konstanta pro použití ve skriptech: "oprasovadlo" Papír – handle: HOpapirH, konstanta pro použití ve skriptech: "papir" Pekáč – handle: HOpekacH, konstanta pro použití ve skriptech: "pekac" Perník – handle: HOpernikH, konstanta pro použití ve skriptech: "pernik" Pero – handle: HOperoH, konstanta pro použití ve skriptech: "pero" Pistole – handle: HOpistoleH, konstanta pro použití ve skriptech: "pistole" Plevel – handle: HOplevelH, konstanta pro použití ve skriptech: "plevel"
125 Plod brambory – handle: HObrambora-pH, konstanta pro použití ve skriptech: "plod brambory" Plod brokolice – handle: HObrokolice-pH, konstanta pro použití ve skriptech: "plod brokolice" Plod celeru – handle: HOceler-pH, konstanta pro použití ve skriptech: "plod celeru" Plod květáku – handle: HOkvetak-pH, konstanta pro použití ve skriptech: "plod kvetaku" Plod mrkve – handle: HOmrkev-pH, konstanta pro použití ve skriptech: "plod mrkve" Plod česneku – handle: HOcesnek-pH, konstanta pro použití ve skriptech: "plod cesneku" Plotna – handle: HOplotnaH, konstanta pro použití ve skriptech: "plotna" Police – handle: HOpoliceH, konstanta pro použití ve skriptech: "police" Postel – handle: HOpostelH, konstanta pro použití ve skriptech: "postel" Počítač – handle: HOpocitacH, konstanta pro použití ve skriptech: "pocitac" Pružina – handle: HOpruzinaH, konstanta pro použití ve skriptech: "pruzina" Pult – handle: HOpultH, konstanta pro použití ve skriptech: "pult" Pytlík – handle: HOpytlikH, konstanta pro použití ve skriptech: "pytlik" Semenáček – handle: HOsemenacekH, konstanta pro použití ve skriptech: "semenacek" Skříň – handle: HOskrinH, konstanta pro použití ve skriptech: "skrin" Sprcha – handle: HOsprchaH, konstanta pro použití ve skriptech: "sprcha" Sprej – handle: HOsprejH, konstanta pro použití ve skriptech: "sprej" Stroj – handle: HOstrojH, konstanta pro použití ve skriptech: "stroj" Stůl – handle: HOstulH, konstanta pro použití ve skriptech: "stul" Talíř – handle: HOtalirH, konstanta pro použití ve skriptech: "talir" Trouba – handle: HOtroubaH, konstanta pro použití ve skriptech: "trouba" Umyvadlo – handle: HOumyvadloH, konstanta pro použití ve skriptech: "umyvadlo" Vypínač – handle: HOvypinacH, konstanta pro použití ve skriptech: "vypinac" Záchod – handle: HOzachodH, konstanta pro použití ve skriptech: "zachod" Záhon – handle: HOzahonH, konstanta pro použití ve skriptech: "zahon" Zásobník – handle: HOzasobnikH, konstanta pro použití ve skriptech: "zasobnik" Šroubovák – handle: HOsroubovakH, konstanta pro použití ve skriptech: "sroubovak"
126
PŘÍLOHA A. PŘEHLED PŘEDMĚTŮ
Štětec – handle: HOstetecH, konstanta pro použití ve skriptech: "stetec" Štětka – handle: HOrajblovadloH, konstanta pro použití ve skriptech: "stetka" Šuplík – handle: HOsuplikH, konstanta pro použití ve skriptech: "suplík" Židle – handle: HOzidleH, konstanta pro použití ve skriptech: "zidle" Žárovka – handle: HOzarovkaH, konstanta pro použití ve skriptech: "zarovka" Česnek – handle: HOcesnek-rH, konstanta pro použití ve skriptech: "cesnek"
127
128
PŘÍLOHA B. PŘEHLED ENTŮ
Příloha B
Přehled entů
Název Zahradník Handle HEzahradnikH Ve skriptu "zahradnik"
Název Kuchař Handle HEkucharH Ve skriptu "kuchar"
Název Policajt Handle HEpolicajtH Ve skriptu "policajt"
Název Hudebník Handle HEhudebnikH Ve skriptu "hudebnik"
Název Opravář Handle HEopravarH Ve skriptu "opravar"
Název Knihovník Handle HEknihovnikH Ve skriptu "knihovnik"
Název Cizák Handle HEcizakH Ve skriptu "cizak"
Název Uživatel Handle HEclovekH Ve skriptu "clovek"
Příloha C
Přehled atomických instrukcí aCtiSi (hknihai) Ent si krátí volnou chvíli. Kód Význam 125 Ent knihu nemá. 126 Kniha je špinavá.
aDejPredmetEntovi (hent, kterému podávámi, hpředmět, který podávámi) Podá předmět entovi. Ten dostane informaci, že mu je předmět podáván. V dalším kole může předmět standardně pomocí aSeber vzít z druhého enta. Toto je jediný přímý způsob přenosu věci z enta na enta. Pokud ent, kterému podávám je HEanyH, potom podávám komukoliv. Kód Význam 13 Ent nemá předmět, který chce podat.
aHraj (hhudební nástroji) Ent zahraje na hudební nástroj. Kód Význam 87 Ent nemá nástroj v ruce.
aHrajHru (hpočítači) Týká se počítače. Ent hraje hru jedno kolo bez jakéhokoliv důsledku. Kód Význam 2 Ent je moc daleko od počítače. 105 Počítač neleží na stole. 106 Počítač je rozbitý. 129
130
PŘÍLOHA C. PŘEHLED ATOMICKÝCH INSTRUKCÍ
aJdi (hdlaždice, kam chcii) Ent popojde na vedlejší dlaždici. Kód Význam 2 Vzdálenost mezi dlaždicemi příliš veliká. 23 Ent se na dlaždici, kam chce, nevejde. 25 Snažíš se jít na něco, co není ani dlaždice ani dveře.
aJez (hjídloi) Sní jednu kalorii z chleba či perníku. Balíček nejde jíst. Kód Význam 44 Ent nemá jídlo. 45 Ent je přežraný. 46 Jídlo je nepoživatelné.
aLehniSi (hposteli) Ent si lehne do postele. Mění se tím staventa na HPstaventa-spiH. Kód Význam 2 Postel je moc daleko. 134 V posteli už něco je.
aMaluj (hpapíri, hštěteci, hbarvyi) Maluje se štětcem na papír, z papíru vzniká obraz, přičemž dojde ke změně handlu předmětu. Kód Význam 2 Ent je moc daleko od papíru. 83 Ent nemá štětec. 84 Papír neleží na stole. 85 Ent je moc daleko od barev. 86 Barvy neleží na stole.
aMixuj (hmixéri) Namixuje jídlo, podle předchozího popisu Kód Význam 2 Ent je moc daleko od mixéru. 53 Mixér není přístupný. 103 Mixer je prázdný,. 104 Na mixéry leží divné věci.
aNamoc (hhadr/hadříki, hkýbl s vodoui) Týká se jen hadru/hadříku. Hadr/hadřík lze namočit jen v kýblu s vodou Kód Význam 2 Ent je daleko od kýblu. 58 Ent nemá hadr. 59 V kýblu není voda.
131 aNandejNaberackou (hhrneci, hnaběračkai, htalíři) Přenese 1 porci jídla z hrnce do talíře. Kód Význam 53 Hrnec je daleko či není přístupný. 53 Talíř je daleko či není přístupný. 79 Ent nemá naběračku. 80 Naběračka či talíř jsou špinavé. 81 Talíř je plný vody nebo nečeho jiného. 82 V hrnci nic není. aNaridBudik (hbudíki, hhandle časui) Nařídí budík na nějaký čas. Čas se udává v kolech. Kód Význam 132 Ent nemá budík v ruce. 133 Budík je rozbitý. aNasypMouku (hpekáči, hmoukai) Nasype mouku do pekáče. Kód Význam 2 Pekáč je příliš daleko. 122 Ent nemá mouku v ruce. 123 Na pekáči už mouka je. 124 Pekáč je špinavý. aNatocVodu (hbateriei, hnádobai) Natočí vodu, baterii přitom v jednom kole vypne i zapne. Vodu lze natočit jen z umyvadla či dřezu a jen do předmětu z množiny nadoby flg. Natočí se vždy maximum vody. Nefunguje, je-li rozbitý generátor vody. Kód Význam 2 Ent je příliš daleko od baterie. 91 Nádoba není v umyvadle. 92 Umyvadlo je rozbité. 93 V nádobě už je voda. 94 Generátor je rozbitý. aOkopej (hzáhoni, hmotyčkai) Okopá záhon. Okopanost je pak stavem zahonu, vlastnost HPokopanostH. Kód Význam 2 Ent je od záhonu příliš daleko. 57 Ent nemá v ruce motyčku. aOpras (hoprašitelný předměti, hoprašovadloi) Viz vlastnost HPcistotaH. Nastaví HPcistotaH na HPcistota-maxH. Zda se daný předmět má čistit oprašovadlem je určeno v poli cimsecisti, viz popis pomocných datových struktur SP. Kód Význam 2 Ent je moc daleko od předmětu. 52 Ent nemá oprašovadlo. 53 Předmět není k dispozici. 54 Oprašovadlo je rozbité.
132
PŘÍLOHA C. PŘEHLED ATOMICKÝCH INSTRUKCÍ
aOpravuj (hrozbitelný předměti, hsprávné nářadíi) Týká se rozbitelných předmětů. Systém opravování je popsán v Dogmatu. Opravování ovšem není v této verzi projektu uvedeno do provozu. SP je však na něj připraven. Kód Význam 2 Ent je moc daleko od opravovaného předmětu. 53 Opravovaná věc není dostupná. 60 Ent nemá nářadí v ruce. aOsetriEnta (hent-ošetřovanýi, hspreji) Vyléčí entovi na jedno použití všechny životy. Kód Význam 2 Ošetřovaný je příliš daleko. 89 Ent nemá sprej v ruce. aOsprchujSe (hsprchai) Ent se osprchuje. Mění se tím jeho vlastnost HPcistotaH. Sprchuje se jedno kolo, nesvléká se. Nefunguje, pokud je rozbitý generátor. Kód Význam 2 Ent je příliš daleko. 94 Generátor je rozbitý. 95 Sprcha je rozbitá. aOtevriVec (hhandle věcii) Otevře předmět. Kód Význam 2 Předmet je od enta buď moc daleko nebo není přístupný nebo ho vlastni jiný ent nebo je v něčem zavřený apod. 3 Předmět není kontejner, nelze ho otevírat/zavírat. 4 Předmět je zamčený. Je třeba nejdříve odemknout. aOtri (hotřitelný předměti, hhadr/hadříki) Týká se otřitelných předmětů. Při otření se nastaví vlastnost HPcistotaH na HPcistota-maxH. Kód Význam 2 Ent je moc daleko. 53 Otřitelný předmět není k dispozici. 58 Ent nemá hadr. aPij (hhrnečeki) Pít lze jenom z hrnečku! Mění se vlastnost HPzizenH u enta. Pokud je hrnek špinavý, ent je po pití zraněn o 1. Kód Význam 3 Ent nemá hrnek. 4 V hrnku není voda.
133 aPoloz (hhandle věcii, hkam pokládám, na/do určeno pomocí hlavního typu handlu, viz 2(str. 13) i) Položí předmět NA/DO nějaké použitelné místo, tj. ne třeba na jiného enta, nebo někam, kde je plno. . . Kód Význam 2 Ent je moc daleko od místa/věci, kam chce předmět položit. 8 Pokus o položení na enta. viz podávání entovi, aDejPredmetEntovi. 9 Na/v předmětu, kam chci pokládat není místo, nebo je tam voda. 10 Pokus o položení předmětu na něco, co není dostupné. 11 Ent nemá předmět, který chce položit. 12 Položení do rozbitého likvidátoru. další kódy Předmět, na který jsem chtěl položit, mě odmítl. aPosadSe (hžidlei) Týká se židle a křesla. Nastaví entovi HPstaventaH na sedění. Ent bude mít pozici na židli. Kód Význam 2 židle je od enta příliš daleko. 137 židle je obsazená. aPosiluj (hpružinai) Ent cvičí s pružinou, aby zabil čas. Kód Význam 77 Ent nemá v ruce pružinu. 78 Pružina je rozbitá. aPoslouchejHudbu () Týká se situace, kdy hudebník v dané místnosti hraje. Enti poslouchají, aby zabili čas. . . Kód Význam 33 V místnosti nikdo nehraje. aPresun (hpředměti) Přesune předmět z ruky do batohu, či z batohu do ruky. Kód Význam 3 Ent danou věc nemá. 4 Ruce jsou plné. aPritluc (hhřebíki, hhandle zdi, kam zatloukámi) Přitluče kladivem hřebík do zdi. Z hřebíku se stane hřebík s příznakem zeď. Pozice tohoto objektu bude ve zdi. Kód Význam 2 Ent je moc daleko od zdi. 55 Ent nemá kladivo nebo hřebík. 56 Ve zdi už něco je.
134
PŘÍLOHA C. PŘEHLED ATOMICKÝCH INSTRUKCÍ
aProjdiDvermi (hdveřei) Instrukce pro procházení dveřmi. Musí se stát na správném místě. Kód Význam 27 Nestojím na správné dlaždici pro průchod dveřmi, nebo dveře nejsou otevřené. 28 Dveřmi nelze projít, neboť za nimi je něco tak velkého, že se ent už nevejde. aPustVodu (hbateriei) Pustí vodu z umyvadla či dřezu, pokud není rozbitý. Je-li ve dřezu nádobí, bude nádobí potom umyté. Voda teče a nic se neděje. Umyté nádobí neobsahuje vodu. Umyté nádobí neobsahuje jídlo. Nefunguje, jeli rozbitý generátor. Kód Význam 2 Ent je příliš daleko od umyvadla-dřezu. 92 Baterie je rozbitá. 94 Generátor je rozbitý. aSazej (hobecný handle rostlinyi, hsemenáčeki, hhandle záhonui, hhandle kolíkui) Zasadí semeno vybrané rostliny na záhon. Sází se ze semenáčku, k zasazení je zapotřebí kolík na udělání díry. Kód Význam 2 Ent je příliš daleko od záhonu. 47 Ent nemá v ruce kolík nebo semenáček. 48 V semenáčku nejsou příslušná semena. 49 Na záhoně už není místo. aSeber (hhandle věcii) Sebere předmět ze země, nebo z nějakého dostupného místa. Instrukcí aSeber bereme i podávanou věc od enta, který ji podává pomocí instrukce aDejPredmetEntovi. Sbírá se do batohu (do) či do ruky (na), podle hlavního typu prvního argumentu (viz 2(str. 13) ). Kód Význam 2 Ent je příliš daleko. 5 Předmět není přenosný. 6 Ent už předmět má, není vlastně chyba. 7 Entův batoh je plný. 53 Předmět je někde zavřený, jiný ent ho má a nepodává. . . . další kód Předmět, kde náš předmět předtím ležel, musí povolit odebrání. aSeberSemena (hrostlinai, hsemenáčeki) Sebere všechna semena z rostliny a dá je do semenáčku. Kód Význam 2 Ent a rostlina jsou příliš daleko. 41 Ent nemá semenáček. 42 Na rostlině nejsou semena.
135 aSmetJidlo (hplný talíři, hkoš, likvidátori) Jídlo je flag talíře. Z talíře ho dostanu jenom tím, že ho smetu buď do koše, nebo přímo do likvidátoru. Kód Význam 2 Ent je od koše/likvidátoru příliš daleko. 12 Likvidátor je rozbitý. 69 Ent nemá talíř u sebe. 70 Na talíři není žádné jídlo. další kódy Koš jídlo nepřijal.
aUmyjSe (humyvadloi) Ent se umyje v umyvadle. Změní se tím vlastnost HPruceH na čisté ruce. Ruce se špiní podle pravidel uvedených v Dogma. Nefunguje, pokud je rozbitý generátor. Kód Význam 2 Ent je příliš daleko. 92 Umyvadlo je rozbité. 94 Generátor je rozbitý.
aUsni () Ent usne v závislosti na únavě, viz Dogma v Příručce autora. Ent někdy usne i sám a nedobrovolně. Kód Význam 29 Ent není unavený.
aUtokBezNoze (hent-cíli) Útočí se viz aUtokBodnou, ale bez bonusu za bodnou zbraň. Kód Význam 2 Enti jsou příliš daleko. 37 útočník má plné ruce.
aUtokBodnou (hent-nepříteli, hnůž/meč v rucei) Ent zaútočí na kolegu nožem nebo mečem. Kód Význam 2 Nepřítel je příliš daleko. 88 Ent nemá nůž/meč v ruce.
aVstan (hna jakou dlaždicii) Týká se zde židle a křesla. Stejnou instrukci se vstává i z postele. Kód Význam 2 Dlaždice je moc daleko. 23 Na zadané dlaždici není místo.
136
PŘÍLOHA C. PŘEHLED ATOMICKÝCH INSTRUKCÍ
aVylejVodu (hz čehoi, hkami) Vylije vodu z nádoby. Vodu není nutné lít jen do jiné nádoby, lije se i do záchodu, sprchy, dřezu a umyvadla. Nic nelze vylít na zem. V nádobách dojde k přenastavení vlastnosti HPvodaH, naliji-li něco jinam. Vyliji-li něco do sprchy, se sprchou se nic nestane. HPvodaH nabývá hodnot určujících počet hrnků vody, které jsou v nádobě. Objemy různých nádob viz Dogma. Přelije se vždy maximum vody. Kód Význam 2 Ent je daleko od toho, do čeho lije. 59 V tom, z čeho liji, není voda. 64 Ent nemá to, z čeho lije. 65 V nádobě, do které liji, už voda je. 66 V nádobě, do které liji, je nějaký předmět.
aVypinac (hvypínači) Zapne/vypne vypínač. Kód Význam 2 Ent je moc daleko od vypínače. 130 Chybí žárovka, nebo je rozbitá. 131 Vypínač je rozbitý.
aVypniVodu (humyvadlo či dřezi) Vypne vodu, která teče z umyvadla či dřezu Kód Význam 2 Ent je příliš daleko od baterie.
aVyrob (hobecný handle předmětu, který chci vyrobiti, hstroji) Vyrobí a dá vyrábějícímu entovi do inventáře. Kód Význam 2 Ent je moc daleko od stroje. 100 Stroj je rozbitý. 101 Požadovaný předmět nelze vyrobit.
aVystrel (hcíl, tj. jiný enti, hpistolei) Ent vystřelí z pistole na cíl. Kód Význam 109 Z pistole nelze vystřelit, je rozbitá ap. 110 V pistoli nejsou náboje. 111 Ent neumí střílet. 135 Ent nemá pistoli v ruce.
137 aVysyp (hkontejneri, hkam – jiný kontejneri) Vysype obsah předmětu někam. Buď celé projde, nebo celé neprojde, tj. testuje se, zdali se všechno najednou vejde. . . Poněkud speciální je případ likvidátoru. Hodím-li něco do likvidátoru, tak to nenávratně zmizí. Koš se chodí vynášet do likvidátoru. Do koše lze vysypat i jídlo, vytváří se tam nevyndatelná hmota, která však zabírá určité místo. Sype se z vnitřku do vnitřku. Kód Význam 2 Ent je příliš daleko od toho, kam chce vysypat. 15 Ent nemá kontejner, který chce vysypat. 16 Kontejner, kam chci sypat to nepojme. 18 Koš jde vysypat jen do likvidátoru. aVytrhni (hkytkai) Rostlina zmizí beze stopy a u enta se objeví plod, který bude mít jiný handle. Typ handle plodu je o 1 vyšší než typ handle bývalé rostliny. Kód Význam 2 Ent je moc daleko. 7 Ent už nemá místo. 43 Rostlina ještě není vyrostlá. aZachod (hzáchodi) Ent musí chodit na záchod, aby se nepotento. Jinak se změní vlastnosti HPcistotaH a HPposranostH. Pokud je záchod rozbitý, bude ent po činu špinavý. Jen v sedě. Kód Význam 2 Ent je příliš daleko od záchodu. aZalej (hzáhoni, hkonevi) Zalévat lze jen z konvice! Zalévá se záhon, nikoliv rostlina. Zalije-li se jednou, HPzalitostH vzroste o 1. Konev se vyprazdňuje o 2 hrnečky vody při každém zalití. Kód Význam 2 Ent je moc daleko od záhonu. 59 V konvi není voda. 73 Ent nemá konev. 74 Záhon už je úplně zalitý, nelze zalévat dál. aZapni (htroubai, hchleba/perníki) Zapne troubu a ta peče, dokud není upečeno. Na tu dobu se zamkne a nepřijímá instrukce, nemůže se rozbít. Při pečení vytvoří buď chleba nebo perník dle zadání. Kód Význam 2 Ent je moc daleko od trouby. 116 Trouba je rozbitá či špinavá. 117 Trouba neobsahuje právě jeden pekáč. 118 Na plotně není právě jeden hrnec. 119 Je už a zapnuto. 120 V troubě je pekáč, ale neobsahuje těsto.
138
PŘÍLOHA C. PŘEHLED ATOMICKÝCH INSTRUKCÍ
aZavriVec (hhandle věcii) Zavře předmět. Kód Význam 2 Předmet je od enta buď moc daleko nebo není přístupný nebo ho vlastni jiný ent nebo je v něčem zavřený apod. 3 Předmět není kontejner, nelze ho otevírat/zavírat. 4 Předmět je zamčený. Je třeba nejdříve odemknout. aZdrimniSi () Ent si zdřímne v posteli za určitých požadavků na únavu daných Dogmatem Kód Význam 29 Ent není unavený. 30 Ent není v posteli.
Příloha D
Přehled vlastností barva předmětu – výčtová vlastnost, handle: HPbarva-anyH, konstanta pro použití ve skriptech: "barva". Vlastnost nabývá následujících hodnot (přístupných pomocí stejnojmenných konstant): HPbarva-bilaH HPbarva-sedaH HPbarva-zlutaH HPbarva-oranzovaH HPbarva-cervenaH HPbarva-zelenaH HPbarva-modraH HPbarva-hnedaH HPbarva-cernaH HPbarva-drevenaH HPbarva-kovovaH hlad – intervalová vlastnost (0 až 20), handle: HPhlad-anyH, konstanta pro použití ve skriptech: "hlad". Minimální a maximální hodnota (HPhlad-minH a HPhlad-maxH) jsou ve skriptech přístupné prostřednictvím konstant "hlad min" a "hlad max". Pro některé speciální hodnoty vlastnosti lze použít následující handly (dostupné pomocí stejnojmenných konstant): HPhlad-hladovyH HPhlad-najedenyH nabitost – intervalová vlastnost (0 až 5), handle: HPnabitost-anyH, konstanta pro použití ve skriptech: "nabitost". Minimální a maximální hodnota (HPnabitost-minH a HPnabitost-maxH) jsou ve skriptech přístupné prostřednictvím konstant "nabitost min" a "nabitost max". Pro některé speciální hodnoty vlastnosti lze použít následující handly (dostupné pomocí stejnojmenných konstant): HPnabitost-nabitaH HPnabitost-nenabitaH název jídla – výčtová vlastnost, handle: HPnazevjidla-anyH, konstanta pro 139
140
PŘÍLOHA D. PŘEHLED VLASTNOSTÍ použití ve skriptech: "nazevjidla". Vlastnost nabývá následujících hodnot (přístupných pomocí stejnojmenných konstant): HPnazevjidla-nicH HPnazevjidla-hnusH HPnazevjidla-moukaH HPnazevjidla-bordelH HPnazevjidla-testoH HPnazevjidla-balicekH HPnazevjidla-vodaH HPnazevjidla-cesneckaH HPnazevjidla-knedloveprozeloH
objem – intervalová vlastnost (0 až 1000), handle: HPobjem-anyH, konstanta pro použití ve skriptech: "objem". Minimální a maximální hodnota (HPobjem-minH a HPobjem-maxH) jsou ve skriptech přístupné prostřednictvím konstant "objem min" a "objem max". odpadky v koši – intervalová vlastnost (0 až 30), handle: HPodpadky-anyH, konstanta pro použití ve skriptech: "odpadky". Minimální a maximální hodnota (HPodpadky-minH a HPodpadky-maxH) jsou ve skriptech přístupné prostřednictvím konstant "odpadky min" a "odpadky max". okopanost – výčtová vlastnost, handle: HPokopanost-anyH, konstanta pro použití ve skriptech: "okopanost". Vlastnost nabývá následujících hodnot (přístupných pomocí stejnojmenných konstant): HPokopanost-okopanyH HPokopanost-neokopanyH ostrost – intervalová vlastnost (-10 až 10), handle: HPostrost-anyH, konstanta pro použití ve skriptech: "ostrost". Minimální a maximální hodnota (HPostrost-minH a HPostrost-maxH) jsou ve skriptech přístupné prostřednictvím konstant "ostrost min" a "ostrost max". osvětlení – výčtová vlastnost, handle: HPosvetleni-anyH, konstanta pro použití ve skriptech: "osvetleni". Vlastnost nabývá následujících hodnot (přístupných pomocí stejnojmenných konstant): HPosvetleni-svetloH HPosvetleni-tmaH otevřenost – výčtová vlastnost, handle: HPotevrenost-anyH, konstanta pro použití ve skriptech: "otevrenost". Vlastnost nabývá následujících hodnot (přístupných pomocí stejnojmenných konstant): HPotevrenost-zamknuteH HPotevrenost-otevreneH HPotevrenost-zavreneH pnutí enta – intervalová vlastnost (-20 až 20), handle: HPwc-anyH, konstanta pro použití ve skriptech: "wc". Minimální a maximální hodnota (HPwc-minH a HPwc-maxH) jsou ve skriptech přístupné prostřednictvím konstant "wc min" a "wc max".
141 posranost – výčtová vlastnost, handle: HPposranost-anyH, konstanta pro použití ve skriptech: "posranost". Vlastnost nabývá následujících hodnot (přístupných pomocí stejnojmenných konstant): HPposranost-posranyH HPposranost-neposranyH povrch – intervalová vlastnost (0 až 1000), handle: HPpovrch-anyH, konstanta pro použití ve skriptech: "povrch". Minimální a maximální hodnota (HPpovrch-minH a HPpovrch-maxH) jsou ve skriptech přístupné prostřednictvím konstant "povrch min" a "povrch max". počet kalorií – intervalová vlastnost (0 až 300), handle: HPkalorie-anyH, konstanta pro použití ve skriptech: "kalorie". Minimální a maximální hodnota (HPkalorie-minH a HPkalorie-maxH) jsou ve skriptech přístupné prostřednictvím konstant "kalorie min" a "kalorie max". počet porcí – intervalová vlastnost (0 až 20), handle: HPpocetporci-anyH, konstanta pro použití ve skriptech: "pocetporci". Minimální a maximální hodnota (HPpocetporci-minH a HPpocetporci-maxH) jsou ve skriptech přístupné prostřednictvím konstant "pocetporci min" a "pocetporci max". počet semen brambor – intervalová vlastnost (0 až 60000), handle: HPpocetsemenbrambor-anyH, konstanta pro použití ve skriptech: "pocetsemenbrambor". Minimální a maximální hodnota (HPpocetsemenbrambor-minH a HPpocetsemenbrambor-maxH) jsou ve skriptech přístupné prostřednictvím konstant "pocetsemenbrambor min" a "pocetsemenbrambor max". počet semen brokolice – intervalová vlastnost (0 až 60000), handle: HPpocetsemenbrokolice-anyH, konstanta pro použití ve skriptech: "pocetsemenbrokolice". Minimální a maximální hodnota (HPpocetsemenbrokolice-minH a HPpocetsemenbrokolice-maxH) jsou ve skriptech přístupné prostřednictvím konstant "pocetsemenbrokolice min" a "pocetsemenbrokolice max". počet semen celeru – intervalová vlastnost (0 až 60000), handle: HPpocetsemenceleru-anyH, konstanta pro použití ve skriptech: "pocetsemenceleru". Minimální a maximální hodnota (HPpocetsemenceleru-minH a HPpocetsemenceleru-maxH) jsou ve skriptech přístupné prostřednictvím konstant "pocetsemenceleru min" a "pocetsemenceleru max". počet semen česneku – intervalová vlastnost (0 až 60000), handle: HPpocetsemencesneku-anyH, konstanta pro použití ve skriptech: "pocetsemencesneku". Minimální a maximální hodnota (HPpocetsemencesneku-minH a HPpocetsemencesneku-maxH) jsou ve skriptech přístupné prostřednictvím konstant "pocetsemencesneku min" a "pocetsemencesneku max". počet semen květáku – intervalová vlastnost (0 až 60000), handle: HPpocetsemenkvetaku-anyH, konstanta pro použití ve skriptech: "pocetsemenkvetaku". Minimální a maximální hodnota (HPpocetsemenkvetaku-minH a HPpocetsemenkvetaku-maxH) jsou ve skriptech přístupné prostřednictvím konstant "pocetsemenkvetaku min" a "pocetsemenkvetaku max".
142
PŘÍLOHA D. PŘEHLED VLASTNOSTÍ
počet semen mrkve – intervalová vlastnost (0 až 60000), handle: HPpocetsemenmrkve-anyH, konstanta pro použití ve skriptech: "pocetsemenmrkve". Minimální a maximální hodnota (HPpocetsemenmrkve-minH a HPpocetsemenmrkve-maxH) jsou ve skriptech přístupné prostřednictvím konstant "pocetsemenmrkve min" a "pocetsemenmrkve max". počet semen rostliny – intervalová vlastnost (0 až 20), handle: HPpocetsemen-anyH, konstanta pro použití ve skriptech: "pocetsemen". Minimální a maximální hodnota (HPpocetsemen-minH a HPpocetsemen-maxH) jsou ve skriptech přístupné prostřednictvím konstant "pocetsemen min" a "pocetsemen max". prečtenost knihy – intervalová vlastnost (-20 až 20), handle: HPprectenost-anyH, konstanta pro použití ve skriptech: "prectenost". Minimální a maximální hodnota (HPprectenost-minH a HPprectenost-maxH) jsou ve skriptech přístupné prostřednictvím konstant "prectenost min" a "prectenost max". Pro některé speciální hodnoty vlastnosti lze použít následující handly (dostupné pomocí stejnojmenných konstant): HPprectenost-prectenaH HPprectenost-neprectenaH sebratelnost – výčtová vlastnost, handle: HPsebratelnost-anyH, konstanta pro použití ve skriptech: "sebratelnost". Vlastnost nabývá následujících hodnot (přístupných pomocí stejnojmenných konstant): HPsebratelnost-sebratelnyH HPsebratelnost-nesebratelnyH spinavé ruce – výčtová vlastnost, handle: HPspinaveruce-anyH, konstanta pro použití ve skriptech: "spinaveruce". Vlastnost nabývá následujících hodnot (přístupných pomocí stejnojmenných konstant): HPspinaveruce-cisteH HPspinaveruce-spinaveH stav enta – výčtová vlastnost, handle: HPstaventa-anyH, konstanta pro použití ve skriptech: "staventa". Vlastnost nabývá následujících hodnot (přístupných pomocí stejnojmenných konstant): HPstaventa-stojiH HPstaventa-sediH HPstaventa-leziH HPstaventa-drimeH, HPstaventa-spiH HPstaventa-mrtevH stav rostliny – výčtová vlastnost, handle: HPstavkytky-anyH, konstanta pro použití ve skriptech: "stavkytky". Vlastnost nabývá následujících hodnot (přístupných pomocí stejnojmenných konstant): HPstavkytky-zadnaH HPstavkytky-malaH HPstavkytky-kveteH HPstavkytky-semenaH HPstavkytky-shnilaH HPstavkytky-uschlaH
143 stav spreje – intervalová vlastnost (0 až 20), handle: HPsprej-anyH, konstanta pro použití ve skriptech: "sprej". Minimální a maximální hodnota (HPsprej-minH a HPsprej-maxH) jsou ve skriptech přístupné prostřednictvím konstant "sprej min" a "sprej max". stav zásobníku – intervalová vlastnost (0 až 5), handle: HPstavzasobniku-anyH, konstanta pro použití ve skriptech: "stavzasobniku". Minimální a maximální hodnota (HPstavzasobniku-minH a HPstavzasobniku-maxH) jsou ve skriptech přístupné prostřednictvím konstant "stavzasobniku min" a "stavzasobniku max". Pro některé speciální hodnoty vlastnosti lze použít následující handly (dostupné pomocí stejnojmenných konstant): HPstavzasobniku-plnyH HPstavzasobniku-prazdnyH střelecké číslo – intervalová vlastnost (-1 až 10), handle: HPsc-anyH, konstanta pro použití ve skriptech: "sc". Minimální a maximální hodnota (HPsc-minH a HPsc-maxH) jsou ve skriptech přístupné prostřednictvím konstant "sc min" a "sc max". typ dveří – výčtová vlastnost, handle: HPjakedvere-anyH, konstanta pro použití ve skriptech: "jakedvere". Vlastnost nabývá následujících hodnot (přístupných pomocí stejnojmenných konstant): HPjakedvere-normalH HPjakedvere-schodyH HPjakedvere-wcH typ knihy – výčtová vlastnost, handle: HPtypknihy-anyH, konstanta pro použití ve skriptech: "typknihy". Vlastnost nabývá následujících hodnot (přístupných pomocí stejnojmenných konstant): HPtypknihy-zabavnaH HPtypknihy-naucnaH velikost – intervalová vlastnost (0 až 1000), handle: HPvelikost-anyH, konstanta pro použití ve skriptech: "velikost". Minimální a maximální hodnota (HPvelikost-minH a HPvelikost-maxH) jsou ve skriptech přístupné prostřednictvím konstant "velikost min" a "velikost max". voda – intervalová vlastnost (0 až 20), handle: HPvoda-anyH, konstanta pro použití ve skriptech: "mnozstvi vody". Minimální a maximální hodnota (HPvoda-minH a HPvoda-maxH) jsou ve skriptech přístupné prostřednictvím konstant "mnozstvi vody min" a "mnozstvi vody max". Pro některé speciální hodnoty vlastnosti lze použít následující handly (dostupné pomocí stejnojmenných konstant): HPvoda-neteceH HPvoda-teceH zalitost – intervalová vlastnost (0 až 10), handle: HPzalitost-anyH, konstanta pro použití ve skriptech: "zalitost". Minimální a maximální hodnota (HPzalitost-minH a HPzalitost-maxH) jsou ve skriptech přístupné prostřednictvím konstant "zalitost min" a "zalitost max". Pro některé
144
PŘÍLOHA D. PŘEHLED VLASTNOSTÍ speciální hodnoty vlastnosti lze použít následující handly (dostupné pomocí stejnojmenných konstant): HPzalitost-zalityH HPzalitost-nezalityH
zapnutost – výčtová vlastnost, handle: HPzapnutost-anyH, konstanta pro použití ve skriptech: "zapnutost". Vlastnost nabývá následujících hodnot (přístupných pomocí stejnojmenných konstant): HPzapnutost-zapnuteH HPzapnutost-vypnuteH zatlučenost – výčtová vlastnost, handle: HPzatlucenost-anyH, konstanta pro použití ve skriptech: "zatlucenost". Vlastnost nabývá následujících hodnot (přístupných pomocí stejnojmenných konstant): HPzatlucenost-zatluceneH HPzatlucenost-nezatluceneH zdraví – intervalová vlastnost (-10 až 10), handle: HPzdravi-anyH, konstanta pro použití ve skriptech: "zdravi". Minimální a maximální hodnota (HPzdravi-minH a HPzdravi-maxH) jsou ve skriptech přístupné prostřednictvím konstant "zdravi min" a "zdravi max". Pro některé speciální hodnoty vlastnosti lze použít následující handly (dostupné pomocí stejnojmenných konstant): HPzdravi-zdravyH HPzdravi-nemocnyH žízen – intervalová vlastnost (0 až 20), handle: HPzizen-anyH, konstanta pro použití ve skriptech: "zizen". Minimální a maximální hodnota (HPzizen-minH a HPzizen-maxH) jsou ve skriptech přístupné prostřednictvím konstant "zizen min" a "zizen max". Pro některé speciální hodnoty vlastnosti lze použít následující handly (dostupné pomocí stejnojmenných konstant): HPzizen-ziznivyH HPzizen-napityH čistota – intervalová vlastnost (-10 až 10), handle: HPcistota-anyH, konstanta pro použití ve skriptech: "cistota". Minimální a maximální hodnota (HPcistota-minH a HPcistota-maxH) jsou ve skriptech přístupné prostřednictvím konstant "cistota min" a "cistota max". únava – intervalová vlastnost (0 až 20), handle: HPunava-anyH, konstanta pro použití ve skriptech: "unava". Minimální a maximální hodnota (HPunava-minH a HPunava-maxH) jsou ve skriptech přístupné prostřednictvím konstant "unava min" a "unava max". Pro některé speciální hodnoty vlastnosti lze použít následující handly (dostupné pomocí stejnojmenných konstant): HPunava-unavenyH HPunava-cilyH útočné číslo – intervalová vlastnost (0 až 20), handle: HPuc-anyH, konstanta pro použití ve skriptech: "uc". Minimální a maximální hodnota (HPuc-minH a HPuc-maxH) jsou ve skriptech přístupné prostřednictvím konstant "uc min" a "uc max".
Příloha E
Popis ukázkového světa Součástí distribuce projektu Enti je mimo jiné ukázkový balík světa, který se nachází v adresáři $ENTIROOT/ukazkovy svet/. Posláním tohoto vzorového balíku je jednak demonstrovat možnosti projektu a jednak poskytnout začínajícím uživatelům vhodné podklady a inspiraci pro samostatnou tvorbu vlastních světů. Svět obsažený v ukázkovém balíku představuje dům o třech podlažích (viz mapa v dodatku Uživatelské dokumentace). V patře se nacházejí obytné pokojíky, knihovna, komora a sociální zařízení. V přízemí pak lze krom jiného nalézt kuchyni, jídelnu, hudební salón a místnost se strojem. Sklep je z velké části tvořen zahradou, nicméně obsahuje i další důležité místnosti: jmenujme především místnosti s likvidátorem a generátorem vody. V domě se vyskytuje množství předmětů. Kromě nábytku a dalšího nezbytného zařízení je možné natrefit na široké spektrum nejrůznějších doplňků porůznu ukrytých ve skříních (hudební nástroje, posilovací pružiny), ležících na stolech (papíry a pera) a policích (knihy) či visících na zdech (obrazy, hodiny). Důležitým předmětem je stroj, který umožňuje výrobu věcí. Na zahradě se nacházejí záhony s rostlinami v různém stadiu růstu. Dům je obýván dvěma enty (nepočítáme-li uživatele): zahradníkem a hudebníkem. Hlavním úkolem zahradníka je péče o rostliny na zahradě – ráno zalévá, odpoledne okopává a navečer sklízí semena a sází nové rostliny. Hudebník již tak pracovitý není, protože většinu času tráví vyhráváním na hudební nástroje. Oba enti – zahradník i hudebník – se starají o své životní potřeby. Kdykoli mají žízeň, uhasí ji, jsou-li hladoví, dojdou se najíst. V případě naléhavé potřeby si odskočí na záchod; pokud to náhodou nestihnou, alespoň se osprchují. Svědomitě si myjí ruce, kdykoli je mají špinavé. Zraní-li se, pokusí se ošetřit. A konečně, jsou-li unavení, půjdou si zdřímnout. V případě nutnosti si enti dokáží ve stroji vyrobit některé předměty, např. chléb nebo sprej pro ošetření. Toplevel skripty pro zahradníka a hudebníka se nacházejí v balíku světa v souborech skripty/zahradnik/zij.e a skripty/hudebnik/zij.e. Tyty skripty jsou ve své podstatě pouze jednoduchými obálkami odvolávajícími se na konkrétní skripty v adresáři skripty/sdilene/: např. okopávání, zalévání, sázení, 145
146
PŘÍLOHA E. POPIS UKÁZKOVÉHO SVĚTA
hraní na hudební nástroj, . . . Jedinou činností, kterou toplevel skripty provádějí, je inicializace a spuštění rozvrhovače úloh (viz kapitola 7(str. 91) ). V rámci jeho inicializace jsou definovány denní úlohy entů a zaregistrovány jejich životní funkce. Po spuštění rozvrhovače přejde život enta plně pod jeho řízení. Soubory zij.e lze využít jako šablonu pro vytváření toplevel skriptů nových entů. Jak u denních úloh, tak i u životních funkcí je možné nastavit jejich prioritu pro plánování. Priorita životních funkcí by měla být vyšší než priorita denních úloh, nicméně nic nebrání vytvoření enta, který o své zdraví pečuje teprve tehdy, nemá-li nic jiného na práci. Priority mohou být buď konstantní, nebo lichoběžníkové (dynamické). Toplevel skripty zahradníka a hudebníka demonstrují použití použití obou typů priorit.