Co myslite, daju sa pomocou DEF FN definovat aj rekurzivne funkcie ? Iste ste
lahko uhadli, ze sa samozrejme daju, ved inak by som vam tuto otazku
nedaval...
Pozrime sa na strukturu takej rekurzivnej funkcie. Na zaciatok si vezmime
nejaky jednoduchy priklad - vypocet faktorial. Faktorialy su definovane
takto: N! = N*(N-1)*(N-2)*...*2*1. Cize slovensky povedane faktorial cisla N
je sucin vsetkych celych kladnych cisel od 1 po N. Tuto funkciu by sme mohli
vypocitat nejak takto:
Ibaze toto je sice napisane velmi pekne, ale do DEF FN sa to zapisat neda, pretoze vo vyraze nemozeme pouzit prikaz IF-THEN. Avsak mozeme pouzit akusi vzdialenu nahradu za IF-THEN - a tou nahradou je binarny operator AND. Tento operator pracuje nasledovne:
Toto sa da velmi pekne vyuzit pri nahrade IF-THEN vo funkciach definovanych "vidlickou". To su take funkcie, ktore maju viac definicii na roznych intervaloch. Napriklad absolutna hodnota by sa dala definovat takto:
Ked to zapiseme do DEF FN a nahradime IF-THEN operatorom AND, bude to vyzerat takto:
| (x AND x>0)-(x AND x<0) |
(x<0) je vyraz, ktory nadobuda hodnotu 0 (akoze nepravda) ak je X nezaporne. Skusme si teraz napisat nejaku inu funkciu, definovanu "vidlickou":
| (x*x AND x <= 0)+(LN x AND x>0) |
Lenze tento zapis pomocou DEF FN ma jednu veliku slabost: ked date pocitacu
vypocitat kolko je FN b(-2) tak z toho zblbne a vypise chybove hlasenie
"Invalid argument". Preco ? Operator AND totiz vyhodnocuje prvy oprerand aj v
pripade, ze druhy je nulovy. To znamena ze funkcia LN sa vyhodnocuje aj vtedy
ak X je zaporne alebo nulove.
Za kazdu cenu musime zabepecit aby sa v pripade X<=0 nevyhodnocovala funkcia
LN. Kedze pri vyhodnocovani aritmetickeho vyrazu sa vzdy vyhodnocuju vsetky
cleny tohto vyrazu, nemozeme tymto sposobom pisat rekurzivne funkcie. Pri
vyhodnocovani vyrazu by sa nam zakazdym zacala vyhodnocovat aj rekurzivna
vetva funkcie, co by viedlo k zacykleniu a preplneniu zasobnika a skoncilo by
to chybovym hlasenim (pripadne kolapsom celeho systemu)...
Aby sme tomu zabranili, musime pouzit nejaky "spinavy" trik. Priklad takeho
"spinaveho" triku je pouzitie funkcie VAL "retazec" spolu s operatorom
"retazec" AND podmienka. Spravime jednu velmi jednoduchu vec: Akonahle bude X
kladne, tak funkcii VAL podhodime na vyhodnotenie retazec "LN x" a v opacnom
pripade jej podhodime retazec "x*x". Nasa "vidlickova" funkcia bude potom
vyzerat takto:
| VAL (("x*x" AND x <= 0)+("LN x" AND x>0)) |
Tymto padom sme zabezpecili aby sa nevyhodnovcovalo LN X pre X<=0. Ked si namiesto LN predstavime rekurzivnu vetvu nejakej funkcie, mozeme tymto sposobom velmi pekne pisat aj rekurzivne funkcie. Napriklad nase faktorialy zapisane rekurzivne budu vyzerat takto:
| VAL (("1" AND n<2)+("n*FN f(n-1)" AND n >= 2)) |
Teraz trochu odbocme od cistej matematiky a skusme si napisat jednu zaujimavu funkciu - nazvime ju "nasobenie retazca cislom". Na vstupe bude mat retazet A$ a cislo N, na vystupe bude davat retazec pozostavajuci z N bezprostredne po sebe iducich retazcov A$. Priklad: Nech A$="zx" a N=4 potom vysledok bude "zxzxzxzx".
| VAL$ (("""""" AND a<1)+("a$+FN m$(a$,a-1)" AND a >= 1)) |
Vsimnite si ze aj tato funkcia je pisana rekurzivne. Tych sest uvodzoviek za
sebou predstavuje retazec pozostavajuci z dvoch znakov - dvoch uvodzoviek.
Prave sest ich je preto lebo ak sa maju v retazci vyskytovat uvodzovky, treba
ich napisat dvakrat.
Pouzitie tejto funkcie je velmi rozmanite. Napriklad chceme vypisat na
obrazovku N znakov '#'. Namiesto klasickeho
FOR a=1 TO N: PRINT "#";: NEXT a |
pouzijeme ovela elegantnejsie
PRINT FN m$("#",N); |
Alebo chceme zarovnavat vypisovane cisla podla praveho okraja. Da sa to spravit takto:
PRINT "Hodnota:"; TAB (31-LEN STR$ x);x |
Lenze ked chceme aby medzi napisom "Hodnota" a samotnym cislom boli napr. bodky, mozeme s vyhodou pouzit nasu funkciu takto:
PRINT "Hodnota";FN m$(".",23-LEN STR$ x);x |
Na zaver si spravme nieco zlozitejsie. Skusme si definovat funkciu "reverse" - obratenie poradia znakov v retazci. Ak bude na vstupe napriklad retazec "abcd", potom na vystupe bude "dcba". Nech tato nasa funkcia pracuje takto: Vezme posledny znak retazca a za neho dopise zvysok retazca, ktory medzi tym uz presiel nasou funkciou "reverse". V pripade retazca dlheho maximalne jeden znak nic netreba obracat a v tom pripade sa vrati ten isty retazec.
| VAL$ (("a$(LEN a$)+FN r$(a$( TO LEN a$-1))" AND LEN a$>1)+("a$" AND LEN a$ <= 1)) |
Funkcia by sa dala definovat este druhym sposobom: Vezmeme prvy znak retazca a tento prvy znak zapiseme za "reverznuty" zvysok retazca. Zapis takto definovanej funkcie bude vyzerat takto:
| VAL$ (("FN o$(a$(2 TO ))+a$(1)" AND LEN a$>1)+("a$" AND LEN a$ <= 1)) |
Na prvy pohlad je vsetko v poriadku a aj tento druhy sposob zapisu by mal bez problemov fungovat. Ale ked si ho vyskusate, zistite ze akosi predsa len nefunguje, hoci v nom nie je ziadna do oci bijuca chyba. V com je ten povestny hacik, preco to nefunguje tak ako by malo, nechavam uz na vasu vlastnu uvahu a badanie. Pre znalcov systemu iste nebude ziadny problem odhalit, v kde je pes zakopany...