Praktická cvičení algoritmů - Informatika Ostravská Univerzita
Transkript
Praktická cvičení algoritmů texty pro distanční studium Ing. Eliška Treterová Ostravská univerzita v Ostravě, Přírodovědecká fakulta Katedra Informatiky a počítačů Ostrava 2003 Praktická cvičení algoritmů 2 Praktická cvičení algoritmů Úvod....................................................................................................................5 1. Jednoduchá proměnná..............................................................................6 1.1. Číselná proměnná ................................................................................6 1.1.1. Celá čísla .....................................................................................6 1.1.2. Příklady použití jednoduché celočíselné proměnné. ...................7 1.1.3. Korespondenční úkol č. 1..........................................................17 1.1.4. Reálná čísla ...............................................................................17 1.1.5. Příklady použití reálné proměnné. ............................................18 1.2. Znaková proměnná ............................................................................22 1.2.1. Znaky.........................................................................................22 1.2.2. Příklady použití znakové proměnné..........................................23 1.3. Logická proměnná.............................................................................28 1.3.1. Logické konstanty .....................................................................28 1.3.2. Příklady použití logické proměnné ...........................................29 1.3.3. Korespondenční úkol č.2...........................................................33 2. Statické datové struktury .......................................................................34 2.1. Řetězec znaků....................................................................................34 2.2. Pole....................................................................................................39 2.2.1. Jednorozměrné pole...................................................................40 2.2.2. Dvourozměrné pole ...................................................................46 2.3. Záznam ..............................................................................................53 2.4. Množina.............................................................................................55 2.5. Korespondenční úkol č. 3..................................................................59 3. Uložení dat mimo operační paměť ........................................................60 3.1. Textový soubor..................................................................................60 3.2. Typový soubor...................................................................................64 3.3. Korespondenční úkol č.4...................................................................70 4. Závěr.........................................................................................................70 5. Literatura.................................................................................................71 3 Praktická cvičení algoritmů 4 Praktická cvičení algoritmů Úvod Studijní text je určen pro zájemce o studium problematiky tvorby algoritmů a jejich zápisu v programovacím jazyku Borland Pascal. Je možné jej využít v předmětu Praktická cvičení algoritmů. Předpokladem úspěšného zvládnutí učiva tohoto studijního textu je dobrá orientace v integrovaném prostředí Borland Pascalu a dobrá znalost základních rysů tohoto programovacího jazyka. Studující musí byt schopen používat jednoduché i strukturované příkazy jazyka Borland Pascal, vytvářet vlastní podprogramy obou dovolených typů – procedury a funkce a znát problematiku datových typů. Studijní text je rozdělen do tří velkých kapitol. V úvodní kapitole jsou vysvětleny základní principy práce s jednoduchou proměnnou. Další kapitola se zabývá problematikou uložení dat pomocí datových struktura a jejich následným zpracováním. Třetí kapitola pak je věnována ukládání dat mimo operační paměť počítače. Studující se zde naučí uložit data do externího souboru a následně pak také zpět do operační paměti. Všechny kapitoly a podkapitoly jsou doplněny velkým množstvím příkladů. U každého příkladu je proveden rozbor řešení a je vloženo řešení ve zdrojovém kódu programovacího jazyka Borland Pascal. 5 Praktická cvičení algoritmů 1. Jednoduchá proměnná Cíl: Po prostudování této kapitoly budete schopni: - používat v programech číselné proměnné - zařadit do algoritmů znakovou proměnnou - rozlišit situace, kdy do algoritmů je vhodně zařadit logickou proměnnou Klíčová slova: Proměnná, celé číslo, reálné číslo, znak, logická proměnná, logická konstanta, aritmetické operace, relační operace, logické operace, logický výraz, standardní podprogramy, datový typ, předefinovaná konstanta. Jednoduchá proměnná slouží k uložení jedné hodnoty. Jako hodnota se zde může objevit celé číslo, desetinné číslo, znak nebo logická konstanta. Význam jednoduché proměnné pro programování spočívá především v tom, že často programátor potřebuje různá počítadla, přepínače a pomocné proměnné pro uložení výsledků a mezivýsledků. 1.1. Číselná proměnná V programovacím jazyku Borland Pascal rozlišujeme čísla celá a čísla desetinná. Obecně platí, že čísla desetinná v programování nazýváme reálná. Rozdělení čísel do těchto dvou skupin vychází z odlišného způsobu jejich vnitřního reprezentace a v závislosti na tom i provádění výpočtu. 1.1.1. Celá čísla Celá čísla se v paměti počítače ukládají přesně. Kladná část čísla je zobrazena ve dvojkové soustavě, záporná čísla se ukládají v doplňkovém kódu. Nejvyšší bit slouží k uložení znaménka. Pokud je tento bit roven nule, je číslo kladné, pokud je roven 1, znamená to, že číslo je záporné. Další bity vymezeného prostoru slouží k uložení vlastní hodnoty. Velikost paměti přidělená jednomu číslu je přesně určena datovým typem proměnné. V Borland Pascalu existuje 5 různých datových typů, kdy nejběžnějším typem je typ INTEGER, který proměnné přiděluje paměť velikosti 16 bitů. Z tohoto prostoru je jeden bit určen pro znaménko a zbývajících 15 bitů pro uložení čísla. Z toho vyplývá, že v tomto prostoru lze uložit libovolné celé číslo z rozmezí -215 až 215-1, tj. -32 768 do 32 767. Překladač Borland Pascalu rozlišuje 5 datových typů pro celá čísla. Typ Množina hodnot (rozsah) Velikost paměti v bytech BYTE 0 .. 255 1 SHORTINT -128 .. 127 1 6 Praktická cvičení algoritmů WORD INTEGER LONGINT 0 .. 65 535 -32 768 .. 32 767 -2 147 483 648 .. 2 147 483 647 1.1.2. 2 2 4 Příklady použití jednoduché celočíselné proměnné. Při použití celočíselné proměnné je vhodné si zapamatovat, které operace Borland Pascal pro celočíselné operace nabízí. Dovolené operace s proměnnými celočíselných typů: a) aritmetické operátory: + … sčítání … odčítání * … násobení DIV … celočíselné dělení MOD… zbytek po celočíselném dělení / … přesné dělení Použití operace dělení pomocí operátoru / je dovoleno pro celočíselné proměnné, ale výsledek tohoto dělení je reálné číslo (číslo s desetinnou částí). Výsledkem ostatních operací bude celočíselná hodnota. Průvodce studiem: Na tomto místě bych vás chtěla upozornit především na existenci operátorů DIV a MOD. Programátor by se bez nich docela lehce obešel, ale proč je nepoužívat, když jsou k dispozici ? Jestliže vypočítáme: 7 : 2 = 3, zbytek je 1 Pak stejné výsledky dostaneme takto: 7 DIV 2 = 3 7 MOD 2 = 1 Vhodnost použití operátorů DIV a MOD a zároveň jejich nahraditelnost je znázorněna v příkladu č.1-1 a příkladu č.1-2. b) relační operátory: = … rovná se <> … nerovná se > … větší než >= … větší nebo rovno < … menší než <= … menší nebo rovno Výsledkem těchto operací je logická hodnota TRUE nebo FALSE ! 7 Praktická cvičení algoritmů Předdefinované celočíselné konstanty - není třeba je zavádět v deklarační části programu. MAXINT - obsahuje největší dovolenou hodnotu , kterou lze uložit do proměnné typu INTEGER, což je 32 767. MAXLONGINT - obsahuje největší dovolenou hodnotu , kterou lze uložit do proměnné typu LONGINT, což je 2 147 483 647. Průvodce studiem: Je výhodné znát, že existují tzv. předdefinované konstanty. Tyto konstanty pak můžete použít, aniž byste je museli uvádět v deklarační část programu a pak také máte jistotu, že mají vždy stejnou hodnotu. V dalším textu jsou občas použity. Příklad 1-1: Zjistěte celou část podílu celých čísel A,B. Předpoklad: máme deklarovány proměnné Vysl, A a B jako celá čísla. Řešení s využitím operátoru DIV: Vysl := A DIV B; Řešení bez použití operátoru DIV: Vysl := trunc(A/B) ; Výsledek obou řešení je stejný ! Druhé řešení je ale zbytečně složité, obvykle se k němu uchýlí programátor, který neví o existenci standardního operátoru DIV. Příklad 1-2: Zjistěte, zda je číslo A dělitelné číslem B. Předpoklad: máme deklarovány proměnné A a B jako celá čísla. Řešení s využitím operátoru MOD: If A MOD B = 0 then { …………}; Řešení s využitím operátoru DIV a zpětné násobení podílu dělitelem: If (A DIV B) * B = K then { …………}; Toto řešení je v podstatě správné, ale zbytečně složité. Řešení bez použití operátoru MOD: If (A DIV B) = A/B then { …………}; If round(A / B) * B = A then { …………}; Tato řešení jsou poněkud nešikovná, protože vyžadují přechod do reálných čísel. 8 Praktická cvičení algoritmů Příklad 1-3: Zjistěte, kolikrát musí jet dopravní prostředek o kapacitě K, aby přepravil skupinu cestujících o počtu P. Zajistěte regulérnost vstupní hodnoty a nabídněte možnost opakování celého výpočtu na základě dotazu. Rozbor řešení: Použijeme aritmetické operace DIV a MOD, které jsou pro celá čísla dovolené. Pomocí DIV zjistíme, kolik bude zcela plných dopravních prostředků, a pomocí MOD zjistíme, zda je třeba poslat další dopravní prostředek, který nebude plný. Řešení v Borland Pascalu: Program PR1_3; uses crt; var K,P,pocet_jizd,zbytek:integer; opakovani:integer; begin repeat clrscr; repeat write('zadej pocet cestujicich: '); readln(P); write('zadej kapacitu dopr.prostredku vetsi nez nula:'); readln(K); until (P>0) and (K>0); pocet_jizd:=p div k; zbytek:= p mod k; if zbytek <> 0 then pocet_jizd := pocet_jizd + 1; writeln('dopr.prostredek musi jet' ,pocet_jizd); writeln; Write('opakovat cely vypocet ? '); Writeln('1=ano, ostatni cisla = konec programu'); readln(opakovani); until opakovani <> 1; end. Průvodce studiem: Algoritmus řešení tohoto problému byl vcelku jednoduchý, hlavně proto, že jste využili operátory DIV a MOD. Do programu jsem zařadila nabídku opakování celého výpočtu. To nemá vliv na vlastní algoritmus řešení, ale uživateli programu tím umožníte pohodlným způsobem testovat různé varianty řešení. Často v programech tuto nabídku naleznete. 9 Praktická cvičení algoritmů Příklad 1-4 Z klávesnice zadáváme předem známý počet celých čísel. Počet vstupních hodnot je uložen v N. Zjistěte kolik ze zadaných hodnot bylo záporných a kolik hodnot bylo dělitelných 5 (použijte operátor MOD). K řešení použijte libovolný příkaz cyklu. Rozbor řešení: Dělitelnost čísla zde chápeme jako situaci, kdy nám po vydělení zůstane zbytek roven nule. K tomuto účelu nám dobře poslouží operátor MOD. Zápornost čísla zjistíme porovnáním čísla s nulou. V programu není zařazena nabídka opakování výpočtu. Pro zamezení automatického návratu do integrovaného prostředí Borland Pascalu po vytištění výsledků je na konci programu vložen příkaz cyklu s podmínkou na konci ve tvaru: repeat until keypressed; kde keypressed je logická funkce, která vrací hodnotu TRUE při stisku libovolné klávesy. Řešení v Borland Pascalu: Program PR1_4; uses crt; var i, cislo, pocet, zap, del:integer; begin Clrscr; Write('Zadejte pocet cisel: '); ReadLn(pocet); del:=0; zap:=0; For i:=1 to pocet do begin Write('Zadejte cislo: '); ReadLn(cislo); if cislo mod 5 = 0 then inc(del); {del:=del+1} if cislo < 0 then inc(zap); end; Writeln('Z celk. poctu ', pocet,' cisel bylo: '); Writeln('zapornych : ',zap); Writeln('delitelnych 5: ',del); repeat until keypressed; end. Příklad 1-5 Sestavte proceduru, která čas zadaný v sekundách převede na hodiny, minuty a sekundy. Načtení času a tisk výsledků bude provedeno v hlavním programu. Zajistěte regulérnost vstupní hodnoty a nabídněte možnost opakování celého výpočtu na základě dotazu. Rozbor řešení: Výsledkem by mělo být vyjádření času v tzv. digitální podobě. Jednoduchým způsobem pomocí operátorů DIV a MOD získáme výsledek. Výpočet má smyl pouze v případě, že na vstupu bude kladné číslo. 10 Praktická cvičení algoritmů Řešení v Borland Pascalu: program PR1_5; uses crt; var pocet_sec:longint; h,m,sec:integer; opakovani:integer; procedure prevod(pocet:longint;var hod,min,sec:integer); var zb:integer; begin hod:= pocet div 3600; zb:=pocet mod 3600; min:= zb div 60; sec:= zb mod 60; end; begin repeat clrscr; repeat write('Zadej cas v sekundach: '); readln(pocet_sec); If pocet_sec <= 0 then writeln('cas zadejte vetsi nez nula!'); until pocet_sec > 0; prevod(pocet_sec,h,m,sec); writeln('Cas v hodinach: ',h,':',m,':',sec); writeln; Write('opakovat cely vypocet ? '); writeln('1=ano, ostatni cisla = konec programu'); readln(opakovani); until opakovani <> 1; end. Samostatný úkol č1: Vyřešte úkol PR1_5 bez použití operátorů DIV a MOD. Rozbor řešení: Celočíselné dělení nahradíme postupným odčítáním dělitele od dělence. Příklad 1-6 Sestavte funkci, která vypočítá N-tou mocninu reálného čísla Z, řešte i pro případ záporného mocnitele. Funkce bude mít dva formální parametry. Tuto funkci využijte v programu. Rozbor řešení: Platí: zn = z * z* z*…z . Pro záporného mocnitele se provede výpočet mocniny s kladným mocnitelem a výsledek pak získáme jako převrácenou hodnotu výsledku. 11 Praktická cvičení algoritmů Průvodce studiem: Překladač jazyka Borland Pascal obsahuje pouze standardní funkci SQR(x), která vypočítá druhou mocninu čísla x. Pokud potřebujete vypočítat N-tou mocninu nebo umocnit číslo na záporného mocnitele, musíte sestavit potřebný algoritmus sami. Algoritmus výpočtu je vcelku jednoduchý, nezapomeňte správně nastavit ve funkci počáteční hodnotu lokální proměnné V na hodnotu 1. Řešení v Borland Pascalu: Program PR1_6; var cis,vysl:real; moc:integer; Function mocnina(z:real;n:integer):real; var i:integer; v:real; begin v:=1; for I:= 1 to abs(n) do v:=v*z; if n<0 then v:= 1/v; mocnina:=v; end; begin Write('Zadej cislo: '); readln(cis); write('Zadej mocnitele: '); readln(moc); vysl:=mocnina(cis,moc); writeln('N-ta mocnina cisla: ',vysl:5:2); writeln; write('zmackni ENTER: '); readln; end. Příklad 1-7 Sestavte funkci pro výpočet N!. Funkce bude mít jeden formální parametr. Funkci využijte v programu – v programu zajistěte, aby se funkce volala pouze v případě, že lze N! vypočítat. Nabídněte možnost opakování celého výpočtu na základě dotazu. Rozbor řešení: Pro faktoriál přirozeného čísla N platí: N! = 1*2*3*4*…*N Pro nulu platí: 0! = 1 Pro záporná čísla není faktoriál definován. Funkce pro výpočet faktoriálu netestuje správnost vstupní hodnoty, to je zajištěno v hlavním programu návratem na opětovné zadání čísla, pokud je číslo záporné. 12 Praktická cvičení algoritmů Průvodce studiem: Ve funkci, kterou sestavíte pro výpočet faktoriálu, musíte zavést dvě pomocné lokální proměnné. Proměnná i slouží jako řídící proměnná cyklu a proměnná v slouží pro uložení výsledku. Teprve na konci funkce přiřaďte obsah proměnné v do názvu funkce. Pokud byste toto přiřazení neprovedli, funkce by vám v místě svého volání nevracela vypočtenou hodnotu. Také je důležité to, že ve funkce netestuje, zda lze či nelze faktoriál ze zadaného čísla vypočítat. To musíte zajistit v programu dříve, než funkci zavoláte. Vzhledem k tomu, že hodnota faktoriálu roste velmi rychle, je důležité, abyste stanovili návratový typ funkce (a také lokální proměnné v na longint. Pokud by ani tento rozsah nestačil, tak zvolte jako návratový typ funkce datový typ real. Nezapomeňte nastavit počáteční hodnotu proměnné v na jedničku. Kdybyste do v dali nulu, byl by výsledek vašeho výpočtu pořád nulový. Řešení v Borland Pascalu: Program PR1_7; uses crt; var F,opakovani:integer; vysl:longint; Function FAKT(n:integer):longint; var i:integer; v:longint; begin v:=1; for i:= 1 to n do v:=v*i; fakt:=v; end; begin repeat clrscr; repeat Write('Zadej cislo N intervalu <0,15> '); readln(f); until (f>=0) and (f<=15); vysl:=fakt(f); writeln('faktorial cisla: ',vysl); writeln; Write('opakovat cely vypocet ? '); writeln('1=ano, ostatni cisla = konec programu'); readln(opakovani); until opakovani <> 1; end. 13 Praktická cvičení algoritmů Příklad 1-8 Sestavte funkci, která bude počítat součin dvou celých čísel pomocí postupného sčítání. Řešte i pro záporné hodnoty. Tuto funkci využijte v programu. Nabídněte možnost opakování celého výpočtu na základě dotazu. Rozbor řešení: Základem řešení je to, že násobení dvou celých čísel je ve své podstatě opakované sčítání jednoho činitele. Počet opakování sčítání je dán hodnotou druhého činitele. Např.: 3*4 = 4+4+4. Pro záporné hodnoty pak je nutné především zajistit, aby výpočet vždy proběhl. To zajišťuje použití standardní funkce ABS(cis1). Po výpočtu je třeba upravit výsledek pro situaci, kdy cis1 je záporné číslo. Ostatní je v pořádku. Např.: 3*4 = 4+4+4 = 12 3*(-4) = (-4) + (-4) + (-4) = -12 -3*4 = 4+4+4 = 12 -3*(-4) = (-4) + (-4) + (-4) =-12 Průvodce studiem: Vyřešení tohoto úkolu je dosti snadné, zvláště, pokud si nepřečtete pozorně zadání a opominete zajistit funkčnost algoritmu i pro záporné hodnoty. Jakmile ale začne student předělávat svůj algoritmus tak, aby dával správné řešení i pro záporné hodnoty, málokdy dojde k řešení, které jsem vám nabídla. Obvykle ke správným výsledkům dospěje, ale v programu bývá velmi mnoho zbytečných rozhodovacích bloků. Proto jsem zařadila tento dosti podrobný rozbor vcelku jednoduchého algoritmu. Řešení v Borland Pascalu: Program PR1_8; uses crt; var c1,c2,opakovani:integer; vysl:longint; Function soucin(cis1,cis2:integer):longint; var i:integer; v:longint; begin v:=0; for i:= 1 to abs(cis1) do v:=v+cis2; if cis1<0 then v:=-v; soucin:=v; end; begin repeat clrscr; write('zadej 1.cinitele: '); 14 Praktická cvičení algoritmů readln(c1); write('zadej 2.cinitele: '); readln(c2); vysl:=soucin(c1,c2); writeln(c1,' * ',c2,' = ',vysl); writeln; Write('opakovat cely vypocet ? '); writeln('1=ano, ostatni cisla = konec programu'); readln(opakovani); until opakovani <> 1; end. Samostatný úkol č.2 Sestavte program, který vypočítá celočíselný podíl a zbytek po celočíselném dělení dvou celých čísel. Řešte i pro záporné hodnoty dělence a dělitele. Příklad 1-9 Sestavte funkci pro výpočet ciferného součtu přirozeného čísla. Použijte v programu, funkci volejte pouze v případě, že číslo je přirozené. Nabídněte možnost opakování celého výpočtu na základě dotazu. Rozbor řešení: Ciferný součet chápeme jako součet jednotlivých cifer zadaného přirozeného čísla. Např: pro číslo 12345 je ciferný součet 10. V algoritmu zjistíme zbytek čísla X po dělení 10 (pomocí MOD), který přičteme do pomocné proměnné, a následně pak číslo X zmenšíme 10 krát (pomocí DIV). Toto vše vložíme do cyklu, který končí v okamžiku, kdy X je rovno 0. Řešení v Borland Pascalu: Program PR1_9; uses crt; var x:longint; soucet,opakovani:integer; Function ciferny_soucet(a:longint):integer; var zb,souc:integer; begin souc:=0; repeat zb:=a mod 10; souc:=souc+zb; a:=a div 10; until a = 0; ciferny_soucet:=souc; end; begin repeat clrscr; repeat Write('Zadej cislo X >= 0 readln(x); until x>=0; 15 '); Praktická cvičení algoritmů soucet:=ciferny_soucet(x); writeln('ciferny soucet : ',soucet); writeln; Write('opakovat cely vypocet ? '); writeln('1=ano, ostatni cisla = konec programu'); readln(opakovani); until opakovani <> 1; end. Příklad 1-10 Určete počet cifer daného přirozeného čísla X. Pro výpočet sestavte vlastní funkci, kterou volejte jen v případě, že X je vyhovující hodnoty. Nabídněte možnost opakování celého výpočtu na základě dotazu. Rozbor řešení: Algoritmus využívá pro zjištění počtu cifer pouze číselné proměnné. Celý postu spočívá v tom, že vstupní hodnota X je postupně v cyklu zmenšována 10 krát (pomocí DIV) až se bude rovnat nule. Počet průchodů cyklem se rovna počtu cifer. Průvodce studiem: Překladač jazyka Borland Pascal nabízí konverzní proceduru STR, která převede číselnou hodnotu do podoby řetězce. Pak pomocí standardní funkce length můžete zjistíte délku této řetězcové proměnné a bude mít v podstatě počet cifer zadaného čísla. Který způsob v praxi použijete závisí na vašem rozhodnutí. Řešení v Borland Pascalu: Program PR1_10; uses crt; var poc,opakovani:integer; cis:longint; Function cifry(cislo:longint):integer; var p:integer; begin p:=0; repeat cislo:=cislo div 10; inc(p); until cislo = 0; cifry:=p; end; begin repeat clrscr; repeat Write('Zadej cislo N >= 0 readln(cis); until cis>=0; 16 '); Praktická cvičení algoritmů poc:=cifry(cis); writeln('pocet cifer: ',poc); writeln; Write('opakovat cely vypocet ? '); writeln('1=ano, ostatni cisla = konec programu'); readln(opakovani); until opakovani <> 1; end. Samostatný úkol č3: Sestavte program, který načte přirozené číslo X, a vypočítá k němu číslo, které má stejné cifry, ale v opačném pořadí. Použijte pouze číselné hodnoty (neprovádějte konverzi na řetězec). 1.1.3. Korespondenční úkol č. 1 Sestavte v programovacím jazyku Borland Pascal jeden ze samostatných úkolů z této kapitoly. 1.1.4. Reálná čísla Pro reálná čísla nabízí překladač Borland Pascalu více datových typů. TYP Množina hodnot (rozsah) Velikost paměti v bytech REAL 2.9 * 10-39 .. 1.7 * 1038 6 -45 38 SINGLE 1.5 * 10 .. 3.4 * 10 4 DOUBLE 5.0 * 10-324 .. 1.7 * 10308 8 -4932 4932 EXTENDED 3.4 * 10 .. 1.1 * 10 10 63 63 COMP -2 + 1 .. 2 - 1 8 Nejpoužívanější je datový typ REAL. Tento datový typ má přesnost 11 až 12 platných číslic. Dovolené operace s proměnnými reálného datového typu: a) aritmetické: + … sčítání … odčítání * … násobení / … dělení Nelze použít operace DIV a MOD !!! b) relační : = <> > >= < <= Výsledkem těchto operací je logická hodnota TRUE nebo FALSE. Předdefinované reálné konstanty - není třeba je zavádět v deklarační části programu. PI – Ludolfovo číslo, které má hodnotu 3,14 17 Praktická cvičení algoritmů 1.1.5. Příklady použití reálné proměnné. Příklad 1-21 Načtěte poloměr R a vypočítejte obvod a obsah kruhu. Ošetřete regulérnost vstupních dat (výpočet má smysl pro R > 0). Nabídněte možnost opakování celého výpočtu na základě dotazu. Řešení v Borland Pascalu: Program PR1_21; var r,opak:integer; obvod,obsah:real; begin repeat repeat write('Zadej polomer (R>=0): '); readln(r); until r>=0; obvod:=2*PI*r; obsah:=PI*sqr(r); writeln('obvod: ', obvod:0:2,' obsah: ',obsah:0:2); writeln; Write('opakovat cely vypocet ? '); writeln('1=ano, ostatni cisla = konec programu'); readln(opak); until opak <> 1; end. Průvodce studiem: Algoritmus výpočtu je založen na triviálních matematických vzorcích. Zařadila jsem jej tady proto, že se již nemůžete vyhnout nutnosti použít reálnou proměnnou. I když zadáte poloměr jako celé číslo, použitím PI se nutně dostanete do oboru desetinných čísel. V souvislosti s reálnou proměnnou narazíte na problém tvaru výpisu čísla na obrazovku. Reálná čísla se vypisuji ve tvaru 0.50000E+1. Pokud chcete, aby se reálné číslo vytisklo ve tvaru se zadaným počtem desetinných míst, musíte vložit do příkazu writeln za název proměnné informaci o počtu míst celkem a o počtu desetinných míst, např.: obsah:10:2 znamená, že číslo na obrazopvce zabere celkem 10 míst a z toho budou dvě desetinná. Příklad 1-22 Sestavte program, který vypočítá kořeny kvadratické rovnice ax2 +bx+c = 0. Program musí pracovat pro libovolné hodnoty koeficientů a, b,c. Rozbor řešení: Vyjdeme z obecného řešení kvadratické rovnice: x1,2 = (-b± √D) /2*a , kde D = b2 – 4*a*c. Nejdříve vypočteme hodnotu diskriminantu D. Pokud je tato hodnota záporná, znamená to komplexně sdružené kořeny kvadratické rovnice. 18 Praktická cvičení algoritmů Pak se musíme soustředit na eventuality, které mohu nastat při zadávání vstupních hodnot. Pokud je a = 0, jedná se o lineární rovnici b*x+c=0 a jejím řešením je kořen: x = -(c/b). Řešení v Borland Pascalu: Program PR1_22; var a,b,c,diskr:real; begin writeln('vypocet korenu kvadraticke rovnice'); writeln('Zadej koeficienty a,b,c:); Readln(a); Readln(b); Readln(c); diskr:=b*b - 4*a*c; If diskr<0 then writeln('Rovnice ma komplexni koreny.') else if a=0 then begin write('Rovnice je linearni s korenem'); writeln('x = ',(-c)/b:10:3); end else begin writeln('Prvni koren=',-b+sqrt(diskr)/2*a:10:3); writeln('Druhy koren=',-b-sqrt(diskr)/2*a:10:3); end; end. Příklad 1-23 Sestavte program pro výpočet přepony pravoúhlého trojúhelníka. Hodnoty obou odvěsen načtěte z klávesnice. Zajistěte regulérnost vstupních dat. Rozbor řešení: K výpočtu přepony je použita Pythagorova věta. V algoritmu je zajištěna regulérnost vstupních dat – hodnota odvěsen musí být větší než nula. Teprve po splnění této podmínky proběhne výpočet. Řešení v Borland Pascalu: Program PR1_23; var odvesnaA,odvesnaB,prepona:real; odp:integer; begin repeat repeat write('zadej 1.odvesnu: '); readln(odvesnaA); until odvesnaA>0; repeat write('zadej 2.odvesnu: '); readln(odvesnaB); until odvesnaB>0; prepona:=sqrt(sqr(odvesnaA)+sqr(odvesnaB)); writeln('prepona: ',prepona:6:2); 19 Praktická cvičení algoritmů writeln; repeat write('opakovat vypocet ? '); writeln('1 = Ano, 2 = Ne'); readln(odp); until (odp=1) or (odp=2); until odp=2; end. Příklad 1-24 Přečtěte hodnotu úhlu ve stupních a napište hodnoty funkcí sinus a kosinus. Po zobrazení výsledků nabídněte možnost opakování celého výpočtu. Po naplnění stránky (asi 20 řádků) výpis zastavte a zajistěte, aby se další stránka vytiskla až po zmáčknutí klávesy ENTER. Rozbor řešení: Překladač jazyka Borland Pascal má standardní funkce sin(x) a cos(x). Tyto funkce pracují s argumenty v radiánech, proto je třeba zadanou hodnotu úhlu přepočítat (1 stupeň = PI/180 rad.). Pro zajištění průchodnosti řešení je v programu zařazeno volání procedury, která provede záměnu dolní a horní hranice intervalu v případě, že dolní hranice je větší než hranice horní. Záměna se provádí bez dotazu nebo upozornění uživatele na chybu. Řešení v Borland Pascalu: program PR1_24; uses crt; var stup1,stup2,i,k:integer; rad:real; procedure zmena(var a,b:integer); var pom:integer; begin pom:=a; a:=b; b:=pom; end; function radian (stupne:integer):real; begin radian:=stupne*(pi/180); end; procedure hlavicka; begin writeln(' Stupne sin(x) cos(x) '); writeln('================================'); end; begin clrscr; write('Zadejte prvni hodnotu: '); readln(stup1); write('Zadejte druhou hodnotu: '); 20 Praktická cvičení algoritmů readln(stup2); if stup1>stup2 then zmena(stup1,stup2); hlavicka; k:=0; for i:= stup1 to stup2 do begin k:=k+1; rad:=radian(i); writeln(' ',i:3,sin(rad):13:5,cos(rad):12:5); if k=20 then begin k:=0; writeln; writeln('Pro pokr. stiskni cokoli.'); readkey; if i<>stup2 then begin clrscr; hlavicka; end; end; end. end; Samostatný úkol č. 4 Doplňte do převodní tabulky další sloupec, ve kterém znázorněte hodnoty funkce tangens. Pro funkci tangens není v prostředí Borland Pascalu standardní funkce, proto musíme tangens vypočítat takto: tg(x) = sin(x)/cos(x). Také musíme dát pozor na to, že funkce tangens není pro některé hodnoty definována. Příklad 1-25 Vypočítejte hodnotu Eulerova čísla e pomocí součtu řady: Výpočet ukončete, až bude rozdíl sousedních členů řady menší než zadaná hodnota, tzn. že bude platit: 1/n – 1/(n+1) < zadaná hodnota. Rozbor řešení: V takto zadaném úkolu neznáme dopředu počet opakování výpočtu, proto můžeme použít příkaz cyklu s podmínkou na začátku nebo s podmínkou na konci. Jako výhodnější se zde jeví příkaz cyklu s podmínkou na konci, jako ukončující podmínka zde slouží porovnání rozdílu Řešení v Borland Pascalu: Program PR1_25; uses crt; var n:integer; e,clen,rozdil:real; begin clrscr; repeat Write('rozdil sousednich clenu 0=konec readln(rozdil); 21 '); Praktická cvičení algoritmů rozdil := abs(rozdil); If rozdil <> 0 then begin e:=0; n:=1; clen:=1; repeat INC(n); clen := clen/n; e:=e + clen; until (clen - clen/n) < rozdil; writeln('e = ',e:10:8); end; until rozdil = 0; end. Průvodce studiem: Proměnná typu Real je pro programátora důležitá. Pokud ale máte jistotu, že při výpočtech zůstanete v oboru celých čísel, využijte toho a reálné proměnné nezavádějte. Použitím celočíselných proměnných bude mít váš program menší nároky na paměť a také výpočty budou rychlejší. 1.2. Znaková proměnná Znaková proměnná dovoluje zařadit do programu práci se znaky. Programátor pak může například zlepšit komunikaci s uživatelem. 1.2.1. Znaky. Do znakové proměnné můžeme uložit libovolný znak z ASCII tabulky. Je rozdíl mezi malým a velkým písmenem, protože malá a velká písmena jsou v ASCII tabulce uložena na jiných místech. Znaková proměnná zabere v operační paměti 1 B. V paměti se ukládá ve dvojkové soustavě číselná hodnota ASCII kódu. ACSII kód je vzájemně jednoznačné přiřazení mezi znakem a číslem (pořadí v ASCII tabulce). Průvodce studiem: Znakovou konstantu poznáme ve zdrojovém textu podle toho, že je znak uzavřen mezi apostrofy. Toto lze ale použít pouze pro zobrazitelné znaky. Pokud budete potřebovat přiřadit nebo vytisknout nezobrazitelný znak, pak musíte znát jeho pořadí v ASCCI tabulce. Zápis pak provedete tak, že napíšete symbol # a celé číslo bez znaménka vyjadřujícího pořadí znaku v ASCII tabulce. Např.: #7 … zvonek nebo #13 … enter Stejný význam jako znak # má funkce CHR( ). #7 a CHR(7) dávají stejný výsledek (zvonek). 22 Praktická cvičení algoritmů Můžeme použít pouze relační operace: = … rovná se <> … nerovná se > … větší než >= … větší nebo rovno < … menší než <= … menší nebo rovno Standardní funkce ORD(x) … funkce, která vrací číslo (pořadí) znaku v ASCII tabulce, tj. číslo z intervalu 0 ..255. CHR(c) … funkce, která vrací znak, který odpovídá danému číslu v ASCII tabulce PRED(x) … funkce, která vrací znak, který v ASCII tabulce má pořadové číslo o 1 menší než znak uvedený jako argument funkce SUCC(x) … funkce, která vrací znak, který v ASCII tabulce má pořadové číslo o 1 větší než znak uvedený jako argument funkce UPCASE(x) ..funkce, která převádí malé písmeno na velké. Pokud je argumentem funkce jiný znak než malé písmeno, zůstává argument beze změny. 1.2.2. Příklady použití znakové proměnné Příklad 1-31 Sestavte program, pro výpis části tabulky ASCII kódů. Zadávejte počáteční a koncovou hodnotu ASCII kódu (vstup omezte na interval 32..255). Po naplnění obrazovky, výpis zastavte. Výstup upravte do tabulky takto: ASCII znak Rozbor řešení: Hodnoty ASCCI kódu se pohybují v intervalu 0..255. Pro každé číslo z tohoto intervalu můžeme pomocí standardní funkce CHR(x) získat odpovídající znak. Omezení dolní hranice intervalu na větší nebo rovno 32 je proto, že znaky s číslem nižším než 32 jsou nezobrazitelné. Řešení v Borland Pascalu: Program PR1_31; uses crt; Var poc,kon,i,radek:integer; procedure zamena(var a,b:integer); var pom:integer; begin pom:=a; a:=b; b:=pom; end; 23 Praktická cvičení algoritmů begin clrscr; repeat write('Zadej poc.hodnotu .. cislo z <32,255>: '); readln(poc); until (poc>=32) and (poc<=255); repeat write('Zadej konc.hodnotu..cislo z <32,255>: '); readln(kon); until (kon>=32) and (kon<=255); zamena(poc,kon); clrscr; writeln(' ASCII znak'); writeln(' ---------------'); radek:=0; for i:=poc to kon do begin writeln( i:8,' ',chr(i)); radek:=radek+1; if radek = 20 then begin write('zmackni ENTER: '); readln; clrscr; radek:=0; writeln(' ASCII znak'); writeln(' ---------------'); end; end; write('zmackni ENTER: '); readln; end. Příklad 1-32 Sestavte program, který načítá z klávesnice znaky. Pokud bude znak malé písmeno, převede jej na písmeno velké pomocí standardní funkce UpCase. Pokud bude znakem velké písmeno, převede jej pomocí vlastní funkce DnCase. Ostatní znaky zůstanou nezměněny. Znaky načítejte opakovaně, program ukončete po zadání znaku *. Rozbor řešení: Funkce pro převod velkého písmena na malé je založena na tom, že rozdíl ASCII hodnoty malého a velkého písmena je stejný pro všechna písmena abecedy. Ve funkci je tento rozdíl uložen do konstanty. Pokud je pak argumentem funkce velké písmeno, dojde k přičtení zjištěného rozdílu k jeho ASCII hodnotě a pak následuje zjištění znaku, který odpovídá vypočtené číselné hodnotě. Řešení v Borland Pascalu: Program PR1_32; uses crt; var znak:char; 24 Praktická cvičení algoritmů Function DownCase(z:char):char; const posun = ord('a') - ord('A'); begin if (z>='A')and(z<='Z') then z:=chr(ord(z)+posun) ; DownCase:=z; end; begin clrscr; znak:=' '; while znak <> '*' do begin write('Zadej znak, * = ukonceni programu: '); readln(znak); if (znak>='A') and (znak <='Z') then znak:=DownCase(znak) else if (znak>='a') and (znak <='z') then znak:=UpCase(znak); writeln('znak po konverzi: ',znak); end; end. Příklad 1-33 Sestavte program, který bude číst větu jako posloupnost písmen a mezer ukončenou tečkou. Zjistěte, kolik má věta písmen. Rozbor řešení: V zadání je uveden požadavek na načítání věty jako posloupnosti písmen. Samozřejmě celou větu chceme zadat na jednom řádku, a proto je nutné pro načítání jednotlivých znaků použít proceduru READ (ne READLN). Po skončení cyklu ale musíme zařadit jednou proceduru READLN bez parametrů, protože potřebujeme „načíst“ klávesu ENTER, která zadávání věty ukončuje. Řešení v Borland Pascalu: Program PR1_33; uses crt; var znak:char; pocet:integer; begin writeln('Zadej vetu ukoncenou teckou !'); repeat read(znak); if (znak <> ' ' ) and (znak <> '.') then INC(pocet); until znak = '.'; readln; writeln('veta ma :',pocet,' pismen'); writeln; writeln('zmackni ENTER'); readln; end. 25 Praktická cvičení algoritmů Průvodce studiem: Načítání věty znak po znaku budete zařazovat, jen když se vám to bude pro řešení algoritmu zdát výhodné. Programovací jazyk Borland Pascal totiž umožňuje pracovat s proměnnou typu STRING a pak je možné provést načtení věty jedním příkazem READLN. Je to rychlejší a pohodlnější. Podrobnější informace a příklady pro práci s proměnnou typu STRING jsou v kapitole 2.1. Řetězec znaků. Příklad 1-34 Sestavte program, který bude číst větu jako posloupnost písmen a mezer ukončenou tečkou. Zkontrolujte, zda po písmenech h,k,r není písmeno i nebo í. Rozbor řešení: Větu zde načítáme opět znak po znaku pomocí příkazu READ a v pomocné logické proměnné si pamatujeme, zda došlo k definované chybě. Pokud chceme v řešení uvažovat malá i velká písmena a také krátká i dlouhá písmena, tak podmínka zjišťující výskyt písmene je dosti dlouhá. Jako elegantní řešení se tady nabízí použití množinového operátoru IN, který znamená „je prvkem“. Relace pak má hodnotu TRUE, pokud proměnná na levé straně výrazu nabude hodnoty uvedené v hranatých závorkách. Řešení v Borland Pascalu: Program PR1_34; uses crt; var znak:char; chyba:boolean; begin chyba := false; writeln('Zadej vetu ukoncenou teckou !'); repeat read(znak); if znak IN ['h','H','k','K','r','R'] then begin read(znak); if znak IN ['i','I',í,Í] then chyba := true; end; until znak = '.'; readln; if chyba then writeln('chyba-vyskyt i po h,k,r') else writeln('Bez chyby !'); writeln; writeln('zmackni ENTER'); readln; end. 26 Praktická cvičení algoritmů Průvodce studiem: Zařadila jsem opět načtení věty znak po znaku, protože práci s proměnnou STRING ještě nemusíte umět, informace získáte až v kapitole 2.1. Řetězec znaků. Velmi pohodlné a přehledné je použití množinového operátoru IN, který můžete použít i bez jakýchkoliv dalších znalosti datového typu množina. Příklad 1-35 Nalezněte maximální hodnotu mezi N zadanými čísly. Využijte příkaz cyklu s podmínkou na začátku. Nabídněte možnost opakování celého výpočtu na základě dotazu (v nabídce využijte datový typ CHAR). Rozbor řešení: Abychom mohli mezi N čísly najít číslo největší, musíme zavést pomocnou proměnnou, ve které budeme uchovávat tzv. okamžité maximum – hodnota, která byla dosud největší. Každou další hodnotu porovnáme s tímto okamžitým maximem a pokud je nová hodnota větší, než hodnota v pomocné proměnné, tak obsah pomocné proměnné přepíšeme touto hodnotou. V předloženém řešení je jako počáteční hodnota použita předdefinovaná konstanta Maxint se záporným znaménkem, což je nejmenší hodnota dovolena pro datový typ INTEGER, a tudíž všechny prohledávané hodnoty by pak měly být větší než je počáteční hodnota pomocné proměnné. Nabídka opakování je řešena pomocí příkazu cyklu s podmínkou na konci, kdy uživatel programu musí zadat konkrétní písmena pro opakování nebo konec. Je zajištěna stejná reakce programu při zadání malých a velkých písmen. Řešení v Borland Pascalu: Program PR1_35; uses crt; var a,max,p,n:integer; odp:char; begin clrscr; max:=-maxint; {maxint je preddefinovana konstanta, ktera ma nejvetsi hodnotu dovolenou pro dat. typ integer} repeat p:=1; write('zadej pocet cisel: '); readln(n); while p<=n do begin write('zadej ',p,'. cele cislo: '); readln(a); p:=p+1; if a>max then max:=a; end; writeln('maximalni hodnota: ',max); writeln; 27 Praktická cvičení algoritmů Write('opakovat cely vypocet ? '); repeat writeln(' A=ano, N = konec programu'); readln(odp); until odp IN ['a','A','n','N']; until (odp = 'n') or (odp = 'N'); end. Průvodce studiem: Algoritmus vyhledání extrémní hodnoty je velmi častým algoritmem. Věnujte mu tedy dostatečnou pozornost. Setkáte se s tímto algoritmem i v dalších úkolech. Vyhledání extrémní hodnoty je součástí některých řadících algoritmů. Samostatný úkol č.5 Upravte program PR1_24 tak, aby došlo k vyhledání minimální hodnota a zároveň ke zjištění, kolikrát se tato minimální hodnota mezi zadanými čísly vyskytla. 1.3. Logická proměnná Logická proměnná dovoluje pracovat v programování s logickou hodnotou. Logická proměnná je sice dobře nahraditelná celočíselnou proměnnou, ale v mnoha případech použití této proměnné zajistí větší přehlednost zdrojového textu a také sníží nároky na paměť. 1.3.1. Logické konstanty V Borland Pascalu jsou zavedeny logické konstanty s názvy true a false. false - nepravda, logická 0 True - pravda, logická 1 Množina operací: a) relační operace = … rovná se <> … nerovná se > … větší než >= … větší nebo rovno < … menší než <= … menší nebo rovno b) logické operace NOT … logická negace AND … logický součin OR … logický součet XOR … neekvivalence (exkluzivní součet) 28 Praktická cvičení algoritmů Pravdivostní tabulka R S FALSE FALSE FALSE TRUE TRUE FALSE TRUE TRUE 1.3.2. NOT R TRUE TRUE FALSE FALSE R AND S R OR S FALSE FALSE FALSE TRUE FALSE TRUE TRUE TRUE R XOR S FALSE TRUE TRUE FALSE Příklady použití logické proměnné Příklad 1-41 Napište program pro opakovaný výpočet druhé mocniny reálného čísla. Pro opakování výpočtu použijte cyklus s podmínkou na začátku a logickou proměnnou KONEC. Rozbor řešení: Algoritmus je velmi jednoduchý, je zařazen jako ukázka použití datového typu boolean v podmínkách podmíněných příkazů a cyklů. Řešení v Borland Pascalu: Program PR1_41; uses CRT; var cislo:real; konec:boolean; begin clrscr; konec:=false; while not(konec) do begin write('zadej cislo: '); readln(cislo); writeln('druha mocnina: ',sqr(cislo):10:2); write('0 = konec, jine cislo = pokracovat: '); readln(cislo); konec:=cislo = 0; end; end. Průvodce studiem: Chtěla bych vás upozornit na příkaz konec:=cislo = 0; , který využívá možnosti přiřazení výsledku logického výrazu do proměnné. Tento příkaz má stejný význam, jako byste napsali: if cislo = 0 then konec := TRUE else konec :=FALSE; 29 Praktická cvičení algoritmů Příklad 1-42 Vytvořte program pro vyhodnocování logických operací NOT, AND a OR. Zadejte druh operace a hodnotu jedné nebo dvou proměnných. Nabídněte možnost opakování celého výpočtu. Rozbor řešení: Hodnotu do logické proměnné nemůžeme načíst příkazem READLN, proto použijeme pomocnou číselnou proměnnou arg_cis, jejíž obsah při načítání omezíme na 0 a 1. Podle obsahu této proměnné pak přiřadíme logickým proměnných hodnotu TRUE nebo FALSE. Řešení v Borland Pascalu: Program PR1_42; uses crt; var funkce,arg_cis,odp:integer; arg_1,arg_2,vysledek:boolean; begin repeat clrscr; writeln(' OR ...0'); writeln(' AND...1'); writeln(' NOT...2'); write('vyber logickou funkci:'); repeat readln(funkce); until funkce IN [0,1,2]; write('zadej argument '); write('0 = False 1 = True : '); repeat readln(arg_cis); until arg_cis IN [0,1]; arg_1 := arg_cis = 1; If not(funkce = 2) then begin write('zadej druhy argument '); write('0 = False 1 = True: '); repeat readln(arg_cis); until arg_cis IN [0,1]; arg_2 := arg_cis = 1; end; case funkce of 0: vysledek := arg_1 or arg_2; 1: vysledek := arg_1 and arg_2; 2: vysledek := not arg_1; end; writeln('vysledek: ',ord(vysledek)); writeln; Write('opakovat cely vypocet ? '); writeln('1=ano, ostatni cisla = konec programu'); readln(odp); until odp <> 1; end. 30 Praktická cvičení algoritmů Příklad 1-43 Napište program pro generování tabulky log. funkcí NOR, NAND a XOR dvou logických proměnných. Rozbor řešení: Tento program nebude po uživateli požadovat žádná vstupní data, výsledkem programu je pravdivostní tabulka požadovaných logických funkcí. Pro logické funkce NOR a NAND není v Borland Pascalu standardní operátor, proto je tedy musíme vyjádřit pomocí operátorů NOT, AND a OR. Pro operaci XOR je sice standardní operátor, ale z cvičných důvodů vyjádříme neekvivalenci jako kombinaci logického součtu, logického součinu a negace. Řešení v Borland Pascalu: Program PR1_43; uses crt; var a_log,b_log,nor,nand,_xor:boolean; begin clrscr; writeln; writeln(' A B NOR NAND XOR'); writeln(' ---------------------------------'); for a_log:=false to true do for b_log:=false to true do begin nor:=not(a_log or b_log); nand:=not(a_log and b_log); _xor:=(a_log and not b_log)or(not a_log and b_log); writeln(a_log:7,b_log:7,nor:7,nand:7,_xor:7); end; writeln; writeln('zmackni ENTER'); readln; end. Průvodce studiem: Tady bych vás chtěla upozornit na využití logické proměnné jako řídící proměnné cyklu FOR. Datový typ boolean patří mezi ordinální datové typy a proto je možné logickou proměnou takto použít. Příklad 1-44 Sestavte program, který po zadání souřadnic bodu v rovině, rozhodne, zda bod leží uvnitř, vně nebo na obvodu kruhu o poloměru R se středem v bodě [0,0]. Nabídněte možnost opakování celého výpočtu. Rozbor řešení: Při řešení vyjdeme z rovnice kruhu: x2+y2 = r2 . Pro body uvnitř platí nerovnost: x2+y2 < r2 Pro body vně platí nerovnost : x2+y2 > r2 31 Praktická cvičení algoritmů Řešení v Borland Pascalu: Program PR1_44; uses crt; const R=10.0; var uvnitr,vne:boolean; x,y:real; odp:byte; begin repeat clrscr; writeln('!!! zadej souradnice bodu !!!'); write('zadej souradnici x: '); readln(x); write('zadej souradnici y: '); readln(y); uvnitr:=sqr(x)+sqr(y)<sqr(R); vne :=sqr(x)+sqr(y)>sqr(R); write('bod lezi '); if vne then write('vne ') else if uvnitr then write('uvnitr ') else write('na obvodu '); writeln('kruhu'); writeln; Write('opakovat cely vypocet ? '); writeln('1=ano, ostatni cisla = konec programu'); readln(odp); until odp <> 1; end. Průvodce studiem: Pokud znáte rovnici kružnice, tak je algoritmus velmi dobře řešitelný. Chtěla jsem vás upozornit na to, jak ovlivní přehlednost zdrojového textu využití přiřazení výsledku logického výrazů do logických proměnných. Následně jsou pak tyto logické proměnné použity v rozhodovacím příkazu. Určitou zajímavostí pro vás může být to, že nemusíte v podmínce příkazu IF psát porovnání logické proměnné s logickou hodnotou TRUE. Úplně stejný význam má zapis: if vne then …. jako zápis If vne = TRUE then … Také si všimněte, že použití vnořeného větvení vás nenutí vyjádřit třetí podmínku, a sice podmínku kdy bod leží na kružnici. Příklad 1-45 Sestavte program, který vypíše všechna prvočísla do zadané hranice. Pro zjištění, zda se jedná o prvočíslo, sestavte logickou funkci, která bude vracet TRUE v případě, že její argument je číslo. Nabídněte možnost opakování celého výpočtu. Rozbor řešení: Prvočísla jsou čísla, která jsou dělitelná pouze sama sebou. Pro zadané číslo K K tedy postupně testujeme, zda je dělitelné 2,3,…až po hodnotu K div 2. Pokud taková dělitelnost nenastane, pak je číslo K prvočíslem. 32 Praktická cvičení algoritmů Řešení v Borland Pascalu: Program PR1_45; uses crt; var n,i,odp:integer; function prvocislo(k:integer):boolean; var i:integer; neni:boolean; begin if k<=2 then prvocislo := true else begin neni:=false; i:=2; repeat neni:=k mod i =0; inc(i); until neni or (i>(k div 2)); prvocislo := not neni; end; end; begin repeat clrscr; writeln('program generuje prvocisla '); write('zadej horni mez intervalu: '); readln(n); for i:= 2 to n do if prvocislo(i) then writeln(i); Write('opakovat cely vypocet ? '); writeln('1=ano, ostatni cisla = konec programu'); readln(odp); until odp <> 1; end. 1.3.3. Korespondenční úkol č.2 Zadání: Upravte program PR1_45 tak, aby se vypisovaly všechny dvojice prvočísel do zadané hranice, jejichž hodnoty se od sebe liší co nejméně, tedy o hodnotu 2. Kontrolní otázky: 1. Které datové typy jsou dovoleny pro proměnnou, do které chcete uložit celé číslo ? 2. Které datové typy jsou dovoleny pro proměnnou, do které chcete uložit reálné číslo ? 3. Které aritmetické operace jsou dovoleny pro celočíselné proměnné ? 4. Které aritmetické operace jsou dovoleny pro celočíselné proměnné ? 33 Praktická cvičení algoritmů 5. 6. 7. 8. Co znamená operátor DIV ? Co znamená operátor MOD ? Co jdou to relační operace a co je jejich výsledkem ? Který datový typ je určen pro deklaraci proměnné, do které chcete uložit znak ? 9. Které operace můžete provést s proměnnou typu CHAR ? 10. Kolik místa zabírá proměnná typu CHAR v paměti počítače? 11. Který datový typ je určen pro deklaraci proměnné, do které chcete uložit logickou hodnotu ? 12. Lze do logické proměnné načíst hodnotu ? 13. Lze obsah logické proměnné tisknout ? 14. Které operace lze provádět s logickou proměnnou ? 15. Co znamená přiřazení výsledku logického výrazu do logické proměnné ? 2. Statické datové struktury Cíl: Po prostudování této kapitoly budete umět: - pracovat s řetězcem znaků - využít v programech možnosti uložení dat ve formě jednorozměrného a dvourozměrného pole - použít proměnnou typu záznam pro uložení a následné zpracování dat - zařadit do zpracování proměnnou typu množina Klíčová slova: Datová struktura, řetězec znaků, pole, záznam, pole záznamů, homogenní a heterogenní datový typ, strukturovaný datový typ, množina hodnot, indexovaná proměnná, tečková konvence. 2.1. Řetězec znaků Proměnná typu STRING je určena pro práci s řetězcem znaků, tzn. že je možné do takto definované proměnné uložit i více než jeden znak (slovo, větu). Proměnná tohoto typu se zavede v deklarační částí programu v úseku Var pomocí klíčového slova STRING. 2 způsoby deklarace proměnné typu STRING: • a:STRING[30]; … do proměnné a se uloží pouze 30 znaků, ostatní budou ignorovány (proměnná v paměti zabere 31 B) • a: STRING; … do proměnné a je možno uložit až 255 znaků proměnná v paměti zabere 256 B 34 Praktická cvičení algoritmů BORLAND PASCAL přidává k proměnné typu STRING na počátek řetězce interně 1 byte, který obsahuje skutečnou délku řetězce. Tato délka je indexována jako nultý byte . Skutečná délka řetězce je uložena ve formě znaku, pro převod do číselné podoby použijeme standardní funkce ORD( ). Délku řetězce lze také zjistit pomocí standardní funkce LENGTH( ). Máme deklaraci … Retez:STRING; Pak platí: ORD( Retez[0] ) = LENGTH(Retez) Retez[0] slouží k uložení aktuální délky řetězce Retez, délka je uložena ve formě znaku. Proměnná typu STRING patří mezi strukturované proměnné, a proto je možno pracovat: • s jednotlivými byty (znaky). Pokud pracujeme s jednotlivými znaky, přistupujeme ke znakům pomocí hranatých závorek. • jako s celkem- existuje mnoho podprogramů pro práci s proměnnou typu string Příklad 2-1 Načtěte z klávesnice větu - pokud začíná věta malým písmenem, tak jej převeďte na velké a pokud na konci chybí tečka, tak ji vložte. Větu vytiskněte. - převeďte všechna písmena na velká a větu opět vytiskněte, každé písmeno na samostatný řádek. - Proveďte obrácení pořadí písmen ve větě, větu vytiskněte. Rozbor řešení: Větu načteme do proměnné STRING jedním příkazem. Dále pak k této proměnné přistupujeme znak po znaku pomocí příkazu cyklu s řídící proměnnou a pomocí indexu v hranatých závorkách. Obrácení řetězce provedeme tak, že postupně zaměňujeme první a poslední znak, pak druhý a předposlední znak, atd. Tady je důležité zastavit cyklus s řídící proměnnou při hodnotě řídící proměnné I = length(veta) div 2 (tedy v polovině řetězce). Kdybychom nechali cyklus proběhnout až do hodnoty I=length(veta), řetězec by nebyl obrácen !!! Řešení v Borland Pascalu: Program PR2_1; uses crt; Var i,delka:byte; veta:string; pom:char; begin clrscr; write('Napis vetu:'); readln(veta); veta[1]:=upcase(veta[1]); delka:=length(veta); if veta[delka] <> '.' then begin veta[delka+1]:= '.'; veta[0]:=chr(delka+1); 35 Praktická cvičení algoritmů end; writeln(veta); writeln; for i:=1 to length(veta) do begin veta[i]:=upcase(veta[I]); writeln(veta[I]); end; for i:=1 to length(veta) div 2 do begin pom:=veta[i]; veta[i]:=veta[length(veta)+1-i]; veta[length(veta)+1-i]:=pom; end; writeln(veta); writeln; write('zmackni ENTER: '); readln; end. Průvodce studiem: Ke každému řetězci se po jeho načtení přidává tzv. nultý byte, ve kterém je uložena aktuální délka řetězce. Pokud jste v programu k řetězci přidali další znaky pomocí operátoru +, např. ve výše uvedeném algoritmu jste přidali na konec tečku, je třeba opravit nultý byte. V programu se jedná o příkaz veta[0]:=chr(delka+1). Pokud neuděláte, nebudou nové znaky při zpracování brány v úvahu. Příklad 2-2 Sestavte program, který přečte větu a zajistí, aby mezi slovy byla pouze jedna mezera. Rozbor řešení: Řetězcovou proměnnou načteme jako celek, ale pak k ní přistupujeme znak po znaku. Upravená věta je na vytvořena v pomocné proměnné veta2. Řešení v Borland Pascalu: Program PR2_2; uses crt; var veta1,veta2:string; i,j,opak:integer; begin repeat clrscr; write('zadej vetu: '); readln(veta1); veta2:=' '; I:=1; j:=1; while i <= length(veta1) do begin veta2[j]:=veta1[i]; if veta1[i]=' ' then while veta1[i+1]=' ' do inc(i); 36 Praktická cvičení algoritmů inc(i); inc(j); end; veta2[0]:=chr(j); writeln(veta2); readln; writeln; write('opakovat cely vypocet ? '); writeln('1=ano, ostatni cisla = konec programu'); readln(opak); until opak <> 1; end. Příklad 2-3 Sestavte program, který načte z klávesnice řetězec znaků o maximální délce 30 znaků. Vyberte z řetězce malá a velká písmena a uložte jej do proměnné Pismena, ostatní znaky uložte do proměnné Ostatní. Mezery vynechejte. Obě proměnné vytiskněte. Rozbor řešení: Pro zjednodušení podmínky, která zjišťuje, zda znak je malé nebo velké písmeno, je použita standardní funkce Upsace. Řešení v Borland Pascalu: Program PR2_3; uses crt; Var i:integer; v,pismena,ostatni:string[30]; begin clrscr; write('Napis vetu:'); readln(v); for I:=1 to length(v) do if ((upcase(v[I]))>='A')and(upcase(v[I])<='Z') then pismena:=pismena+v[i] else if v[I] <> ' ' then ostatni:=ostatni+v[I]; writeln; writeln('zadana veta : ',v); writeln('pismena : ',pismena); writeln('ostatni znaky : ',ostatni); writeln; write('zmackni ENTER: '); readln; end. Příklad 2-4 Z klávesnice načtěte do jedné proměnné jméno a příjmení v nezkrácené podobě (např. JAN NOVAK). Výsledkem práce programu bude: • zkratka jména s plným příjmením (J. NOVAK) • zkratka jména a zkratka příjmení (J. N.) • příjmení a jméno v nezkrácené podobě (NOVAK JAN) Využijte standardní procedury a funkce pro práci s řetězcem jako s celkem (např. DELETE, INSERT,POS ...). 37 Praktická cvičení algoritmů Rozbor řešení: Při řešení tohoto úkolu jsou využity standardní procedury a funkce, které pracuji s řetězcem jako s celkem. Přesný význam jednotlivých podprogramů je uveden v nápovědě Borland Pascalu. Řešení v Borland Pascalu: program PR2_4; uses crt; var jmeno,jm1,jm2,jm3:string[30]; i,del:integer; k:char; begin repeat clrscr; repeat write('Zadejte jmeno : '); readln(jmeno); i:=pos(' ',jmeno); if i=0 then writeln('Jmeno musi obsahovat mezeru.') until i<>0; jm1:=jmeno; delete(jm1,2,(i-1)); insert('.',jm1,2); writeln(jm1); del:=length(jm1); delete(jm1,4,(del)); insert('.',jm1,4); writeln(jm1); jm2:=copy(jmeno,1,(i)); delete(jmeno,1,(i)); del:=length(jmeno); insert(jm2,jmeno,(del+1)); insert(' ',jmeno,(del+1)); writeln(jmeno); write('Chcete opakovat(A-opakovat, ostatni-ne)'); readln(k); until Upcase(k) <> 'A'; writeln; writeln('Konec programu. Stiskni ENTER.'); readln; end. Příklad 2-5 Sestavte program, který načte datum ve tvaru DD-MMM-RR, kde MMM je zkratka anglického názvu měsíce, např. 01-AUG-03, a toto datum převede do tvaru: 1.8.2003. Rozbor řešení: Při řešení je zde využita konstanta, která je tvořena řetězcem znaků. V řetězci jsou za sebou uložena postupně vždy tři písmena, která jsou zkratkou anglického názvu měsíců. 38 Praktická cvičení algoritmů Řešení v Borland Pascalu: Program PR2_5; uses crt; const mesice = 'JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC'; Var angl_datum:string[10]; den,mesic:String[2]; rok:string[4]; datum:string[10]; mes,i,opak:integer; pom:String[3]; begin repeat clrscr; writeln('zadej datum ve tvaru dd-mmm-rr, kde mmm je anglicka zkratka mesice'); readln(angl_datum); for I:=1 to length(angl_datum) do angl_datum[I]:=upcase(angl_datum[I]); den:=copy(angl_datum,1,2); if den[1] = '0' then delete(den,1,1); rok:=copy(angl_datum,8,2); rok:= concat('20',rok); pom:=copy(angl_datum,4,3); mes:=pos(pom,mesice); mes:=(mes+2) div 3; str(mes:2,mesic); datum:=den+'.'+mesic+'.'+rok; writeln; writeln(datum); writeln; Write('opakovat cely vypocet ? '); writeln('1=ano, ostatni cisla = konec programu'); readln(opak); until opak <> 1; end. Samostatný úkol č. 6 Vytvořte program pro jednoduchou hru, kdy jeden z hráčů zadá jméno českého města a druhý podle počátečního písmene a délky názvu hádá. K dispozici má 5 pokusů, pak se jméno objeví na obrazovce. 2.2. Pole Nejznámějším představitelem datové struktury je pole údajů. Je to datová struktura, která má předem pevně stanovený počet prvků, proto také říkáme, že je to statická datová struktura. Položky se vzájemně rozlišují pomocí indexu. V operační paměti tato struktura zabírá souvislý úsek. Datový typ pole je homogenní datový typ, což znamená, že všechny jeho prvky jsou stejného datového typu. 39 Praktická cvičení algoritmů Pole je strukturovaný datový typ a proto platí, že s proměnnou můžeme pracovat jako s celkem nebo s jejími jednotlivými komponentami. a) práce s polem jako s celkem Lze provést pouze operaci přiřazení. Přiřazení lze provést, pokud jsou obě pole totožného typu. Platí stejná pravidla jako u jednorozměrného pole. b) práce s jednotlivými prvky pole můžeme provádět takové operace, které jsou dovoleny pro datový typ, který jsme pro prvky pole nadeklarovali 2.2.1. Jednorozměrné pole Při deklaraci proměnné typu pole se určuje jeho název, rozměr a datový typ prvků pole. Rozměr pole určuje počet prvků, které se do pole mohou vložit. Velikost pole je vždy omezena paměťovými nároky. Jednorozměrné pole odpovídá matematickému objektu vektor. Příklad 2-21 Pole o N prvcích naplňte náhodnými čísly z intervalu <1,10>, prvky pole vytiskněte. Hodnotu N zadávejte z klávesnice. Sestavte funkci, která vypočítá aritmetický průměr prvků pole. Vytvořte také vlastní procedury pro naplnění pole a pro vytištění pole – obě budou mít jako formální parametr pole. Rozbor řešení: Pokud chceme používat v programu podprogramy s formálními parametry typu pole, je nutné deklarovat vlastní datové typy v úseku TYPE a ty pak používat pro deklaraci globálních, lokálních proměnných a formálních parametrů. Dosáhneme tím kompatibility datových typů. Ve zdrojovém textu jsou vloženy procedury pro naplnění pole a pro vytištění pole, a funkce pro výpočet aritmetického průměru všech prvků pole. Aritmetický průměr dostaneme jako součet všech prvků dělený počtem prvků. Protože počítáme přesný podíl, dostaneme se do oboru reálných čísel, a proto musí být návratový typ funkce REAL. Řešení v Borland Pascalu: program PR2_21; {aritmeticky prumer} uses crt; type tpole = array[1..100] of integer; var i,n:integer; pole:Tpole; 40 Praktická cvičení algoritmů Procedure Napln(var A:Tpole;n:integer); var I:integer; begin randomize; for I:=1 to n do A[I]:=random(10)+1; end; Procedure Tiskni(A:Tpole;n:integer); var I:integer; begin for I:=1 to n do write(A[I]:3); writeln; end; Function prumer(a:Tpole;n:Integer):real; var I:integer; v:real; begin v:=0; for i:= 1 to n do v:=v+a[i]; prumer:=v/n; end; begin clrscr; write('zadej pocet prvku pole: '); readln(n); Napln(pole,n); Tiskni(pole,n); writeln; writeln('prumerna hodnota : ',prumer(pole,n):0:2); writeln; writeln('zmackni enter'); readln; end. Příklad 2-22 Sestavte program, který pole A o 20 prvcích naplní náhodnými čísly z intervalu <1,20>, pole vytiskněte. Pak do pole SUDA přepište čísla sudá a do pole LICHA přepište čísla lichá. Obě pole vytiskněte. Rozbor řešení: Protože ještě neumíme deklarovat rozměr pole až v průběhu výpočtu, musí mít všechna pole – vstupní pole, pole pro čísla sudá i pole pro čísla lichá – stejný rozměr. Aby byla pole pro čísla sudá a pro čísla lichá zaplňována správně, zavedeme si samostatný index pro každé pole. Řešení v Borland Pascalu: program PR2_22; {prepis do pole SUDA a LICHA} uses crt; const N = 20; type Tpole = array [1..n] of integer; 41 Praktická cvičení algoritmů var i,j,k:integer; a,suda,licha:Tpole; Procedure Napln(var A:Tpole;n:integer); var I:integer; begin randomize; for I:=1 to N do A[I]:=random(10)+1; end; Procedure Tiskni(A:Tpole;n:integer); var I:integer; begin for I:=1 to N do write(A[I]:3); writeln; end; begin clrscr; Napln(a,n); Tiskni(a,n); writeln; j:=0; k:=0; for i:= 1 to N do if a[i] mod 2 = 0 then begin j:=j+1; suda[j]:=a[i]; end else begin k:=k+1; licha[k]:=a[i]; end; write('suda cisla : '); tiskni(suda,j); writeln; write('licha cisla: '); tiskni(licha,k); writeln; writeln; write('zmackni ENTER'); readln; end. Příklad 2-23 Pole o N prvcích naplňte náhodnými čísly z intervalu <1,100>, prvky pole vytiskněte. Napište proceduru, která z daného pole vybere minimální a maximální hodnotu. Obě tyto hodnoty procedura vrátí jako výsledek. Rozbor řešení: Algoritmus vyhledání extrémní hodnoty je popsán v programu PR1_35. Postup je stejný, rozdíl je pouze v uložení hodnot v paměti. 42 Praktická cvičení algoritmů Řešení v Borland Pascalu: program PR2_23; {maximalni a minimalni hodnota v poli} uses crt; const N = 20; type tpole = array[1..n] of integer; var i,mx,mn,poc:integer; pole:Tpole; procedure nacti(var a:Tpole;n:integer); var i:integer; begin randomize; for i:= 1 to N do a[i]:=random(100)+1; end; procedure vytiskni(a:Tpole;n:integer); var i:integer; begin for i:= 1 to N do write(a[i],' '); writeln; end; procedure max_min(a:Tpole; n:integer; var max,min:integer); var i:integer; begin max:= a[1]; min:= a[1]; for i:= 2 to N do begin if a[i] > max then max:=a[i]; if a[i] < min then min:=a[i]; end; end; begin clrscr; Write('zadej pocet prvku pole: '); readln(poc); nacti(pole,poc); vytiskni(pole,poc); max_min(pole,poc,mx,mn); writeln('maximalni hodnota v poli: ',mx); writeln('minimalni hodnota v poli: ',mn); writeln; writeln('zmackni enter'); readln; end. 43 Praktická cvičení algoritmů Průvodce studiem: Algoritmus vyhledání extrémní hodnoty je zde zařazen znovu z toho důvodu, že častější způsob uložení dat je ve formě datové struktury a ne tak jak je provedeno v PR1_35.Všimněte si také počáteční hodnoty v pomocných proměnných MAX a MIN. Do pomocných proměnných jsou uloženy hodnoty prvního prvku pole a porovnávání dalších hodnot s těmito pomocnými proměnnými začíná od druhého prvku pole. Příklad 2-24 Sestavte program, který vygeneruje N čísel z intervalu <1,10>. Zjistěte četnost výskytu jednotlivých číslic. Rozbor řešení: Četnost výskytu znamená, kolikrát se to které číslo vyskytlo. V řešení se vychází z předpokladu, že vstupní hodnoty se generují z intervalu <1,10>, proto pro uložení četnosti výskytu čísel je připraveno pole CETNOST o 10 prvcích. Podle hodnoty vygenerovaného čísla se pak zvyšuje o jedničku hodnota toho prvku pole CETNOST, který má index shodný s vygenerovanou hodnotou. Řešení v Borland Pascalu: program PR2_24; uses crt; const N = 100; type tpole = array[1..N] of integer; Tpole1 = array[1..10] of integer; var i,poc:integer; A:Tpole; cetnost:Tpole1; procedure nacti(var a:Tpole;n:integer); var i:integer; begin randomize; for i:= 1 to N do a[i]:=random(10)+1; end; procedure vytiskni(a:Tpole;n:integer); var i:integer; begin for i:= 1 to N do write(a[i],' '); writeln; end; begin clrscr; Write('zadej pocet prvku pole: '); readln(poc); 44 Praktická cvičení algoritmů nacti(A,poc); vytiskni(A,poc); for I:=1 to poc do cetnost[A[I]]:=cetnost[A[I]]+1; for I:= 1 TO 10 do if cetnost[I]<>0 then writeln(I:3,' ',cetnost[I]); writeln; writeln('zmackni enter'); readln; end. Příklad 2-25 Sestavte program, ve kterém naplňte pole o N prvcích náhodnými čísly z intervalu <1,50>. Pak načtěte z klávesnice číslo X (zajistěte opakování načtení v případě, že zadané číslo leží mimo interval <1,50> ) a zjistěte, zda toto číslo či není uloženo v poli (sestavte logickou funkci). Rozbor řešení: Při vyhledávání konkrétní hodnoty je vhodné zajistit, aby prohledávání skončilo při nalezení hodnoty. Proto je v řešení zařazen jiný cyklus než cyklus s řídící proměnnou. Musíme zajisti také, aby prohledávání skončilo, pokud hledaná hodnota v poli není. Řešení v Borland Pascalu: program PR2_25; uses crt; const N = 100; type tpole = array[1..N] of integer; var i,poc,x:integer; A:Tpole; ano:boolean; procedure nacti(var a:Tpole;n:integer); var i:integer; begin randomize; for i:= 1 to N do a[i]:=random(50)+1; end; procedure vytiskni(a:Tpole;n:integer); var i:integer; begin for i:= 1 to N do write(a[i],' '); writeln; end; begin clrscr; Write('zadej pocet prvku pole: '); readln(poc); nacti(A,poc); vytiskni(A,poc); repeat write('zadej cislo z intervalu <1,50> : '); Readln(x); until x in [1..50]; 45 Praktická cvičení algoritmů I:=1; Ano:=false; while (I<=poc) and not Ano do begin if a[i]=x then ano:=true; inc(i); end; if ano then writeln('cislo ',x,' je v poli ulozeno') else writeln('cislo ',x,' neni v poli ulozeno'); writeln; writeln('zmackni enter'); readln; end. Průvodce studiem: Uložení dat v paměti formou jednorozměrného pole je pro programátora hodně důležité. Naučte se tento způsob práce efektivně využívat. Při práci s polem dávejte pozor na to, abyste se nedostali mimo deklarovanou oblast a nesnažili se pracovat s paměti, která není pro pole alokována. Pokud máte pole indexováno např. pro 5 prvků (A:array[1..5] of integer) a provedete uložení hodnoty do A[6], překladač nebude hlásit žádnou chybu a dojde k uložení hodnoty do paměti. Nemusí tím dojít k žádným problémům, ale můžete si přepsat jiná důležitá data a způsobit si naprosto nečekanou reakci programu. Samostatný úkol č. 7 Sestavte program, který pole o N prvcích naplní náhodnými čísly a nalezne dvě největší hodnoty v poli. 2.2.2. Dvourozměrné pole Borland Pascal dovoluje, aby prvky pole měly opět strukturu pole. Tímto způsobem pak vznikne pole polí, nebo-li dvourozměrné pole. Dvojrozměrné pole odpovídá matematickému objektu matice. Jedná se o strukturovaný datový typ a proto platí, že s proměnnou můžeme pracovat jako s celkem nebo s jejími jednotlivými komponentami. Průvodce studiem: Uložení dat ve formě dvourozměrného pole budete potřebovat v situaci, že data tvoří jakousi tabulku. Musíte ale pamatovat na to, že se jedná o homogenní datový typ, což znamená, že všechny hodnoty v poli musí být stejného typu. V nabídnutých příkladech jsou pole vždy naplněna náhodnými čísly bez návaznosti na nějaké konkrétní využití v praxi. Cílem je, abyste pochopili indexaci této proměnné a bezpečně zvládli zpracování takto uložených dat. 46 Praktická cvičení algoritmů Příklad 2-26 Naplňte dvojrozměrné pole o N řádcích a M sloupcích náhodnými čísly a vytiskněte ve tvaru matice. Vypočítejte součet prvků v každém řádku a součet prvků v každém sloupci. Rozbor řešení: Pro naplnění a tisk pole jsou zde sestaveny procedury. Pro výpočet potřebných součtů jsou deklarována dvě jednorozměrná pole, jejichž rozměr odpovídá počtu řádků a počtu sloupců. Výpočet součtů se provede najednou při jednom průchodu polem. Řešení v Borland Pascalu: program PR2_26; uses crt; type Tpole = array[1..20,1..30] of integer; Tradek= array[1..20] of integer; Tsloup= array[1..30] of integer; var pole:Tpole; i,j,m,n:integer; rad:Tradek; slo:Tsloup; procedure napln(var a:Tpole); var i,j:integer; begin randomize; for i:= 1 to M do for j:=1 to N do a[i,j]:=random(10)+1; end; procedure vytiskni(a:Tpole); var i,j:integer; begin for i:= 1 to M do begin for j:=1 to N do write(a[i,j]:3); writeln; end; end; procedure soucty(a:Tpole;var rad:Tradek;var Sl:Tsloup;m,n:integer); var i,j:integer; begin for i:= 1 to m do for j:=1 to n do begin rad[i]:=rad[i]+a[i,j]; sl[j]:=sl[j] +a[i,j]; end; end; 47 Praktická cvičení algoritmů begin clrscr; write('zadej pocet radku: '); readln(M); write('zadej pocet sloupcu: '); readln(N); napln(pole); vytiskni(pole); writeln; soucty(pole,rad,slo,M,N); for i:= 1 to M do writeln('soucet ',i,'.radku: ',rad[i]); writeln; for i:= 1 to N do writeln('soucet ',i,'.sloupce: ',slo[i]:3); writeln; writeln; write('zmackni ENTER: '); readln; end. Příklad 2-27 Naplňte čtvercovou matici celými čísly, vytiskněte ve tvaru matice. Sestavte funkci, která vypočítá součet prvků na hlavní diagonále. Pak sestavte funkci, která vypočítá součet prvků na vedlejší diagonále. Výsledky vytiskněte. Rozbor řešení: Pod pojmem hlavní diagonála rozumíme prvky ležící na úhlopříčce zleva doprava. Tyto prvky mají řádkový a sloupcový index stejný. Vedlejší diagonálu tvoří prvky ležící na úhlopříčce zprava doleva. Řádkový index těchto prvků začíná 1 a zvyšuje se o hodnotu jedna až do zadaného řádu pole (N). Sloupcový index začíná číslem, které vyjadřuje zadaný řád pole (N) a snižujeme se o 1 až po hodnotu 1. Řešení v Borland Pascalu: program PR2_27; {soucet prvku na hlavni a vedlejsi diagonale} uses crt; type Tpole = array[1..20,1..20] of integer; var pole:Tpole; hlavni,vedlejsi,n:integer; procedure napln(var a:Tpole); var i,j:integer; begin randomize; for i:= 1 to n do for j:=1 to n do a[i,j]:=random(10)+1; end; procedure vytiskni(a:Tpole); var i,j:integer; 48 Praktická cvičení algoritmů begin for i:= 1 to n do begin for j:=1 to n do write(a[i,j]:3); writeln; end; writeln; end; function hlavni_diagonala(a:Tpole):integer; var i,s:integer; begin s:=0; for I:=1 to n do s:=s+a[i,i]; hlavni_diagonala:=s; end; function vedlejsi_diagonala(a:Tpole):integer; var i,j,s:integer; begin s:=0; j:=n; for I:=1 to n do begin s:=s+a[i,j]; j:=j-1; end; vedlejsi_diagonala:=s; end; begin clrscr; write('zadej rad matice: '); readln(n); napln(pole); vytiskni(pole); hlavni:=hlavni_diagonala(pole); vedlejsi:=vedlejsi_diagonala(pole); writeln('soucet na hlavni diagonale: ',hlavni); writeln('soucet na vedlejsi diagonale: ',vedlejsi); writeln; write('zmackni ENTER: '); readln; end. Příklad 2-28 Naplňte čtvercovou matici čísly, vytiskněte. Napište proceduru na výměnu řádků v matici. Procedura bude mít 3 parametry. Prvním parametrem je matice, dalšími dvěmi parametry jsou celá čísla v rozmezí od 1 do N. Procedura vymění i-tý a j-tý řádek. Výslednou matici opět vytiskněte. Rozbor řešení: Záměna zadaných řádku matice je provedena pomocí procedury VYMEN, která pracuje s celočíselnými hodnotami. Tato procedura je dobře použitelná, při jejím volání se formální parametry nahradí konkrétními prvky pole. 49 Praktická cvičení algoritmů Řešení v Borland Pascalu: program PR2_28; {vymena radku v matici} uses crt; const n = 5; type Tpole = array[1..n,1..n] of integer; var pole:Tpole; r1,r2:integer; procedure napln(var a:Tpole); var i,j:integer; begin randomize; for i:= 1 to n do for j:=1 to n do a[i,j]:=random(10)+1; end; procedure vytiskni(a:Tpole); var i,j:integer; begin for i:= 1 to n do begin write(i,'. radek: '); for j:=1 to n do write(a[i,j]:3); writeln; end; writeln; end; procedure vymen(var a:Tpole; k,l:integer); var pom,i,j:integer; begin for j:= 1 to n do begin pom:=a[k,j]; a[k,j]:=a[l,j]; a[l,j]:=pom; end; end; begin clrscr; napln(pole); vytiskni(pole); repeat write('zadej cisla radku, ktere se maji vymenit(1 - 5): '); readln(r1,r2); until (r1>=1) and (r1<=N) and (r2>=1) and (r2<=N); vymen(pole,r1,r2); writeln('provedla se zamena ',r1,'. a ',r2,' . radku'); writeln; vytiskni(pole); writeln; write('zmackni ENTER: '); readln; end. 50 Praktická cvičení algoritmů Příklad 2-29 Naplňte čtvercovou matici celými čísly, vytiskněte ve tvaru matice. Sestavte funkci, která vypočítá součet prvků pod hlavní diagonálou. Rozbor řešení: Hlavní diagonálu tvoří prvky ležící na úhlopříčce zleva doprava. Tyto prvky mají index řádku stejný jako index sloupce. Pro všechny prvky pod hlavní diagonálou platí, že mají index řádku větší než index sloupce. Řešení v Borland Pascalu: program PR2_29; uses crt; type Tpole = array[1..20,1..20] of integer; var pole:Tpole; vysl,N:integer; procedure napln(var a:Tpole;N:integer); var i,j:integer; begin randomize; for i:= 1 to N do for j:=1 to N do a[i,j]:=random(10)+1; end; procedure vytiskni(a:Tpole;N:integer); var i,j:integer; begin for i:= 1 to N do begin for j:=1 to N do write(a[i,j]:3); writeln; end; writeln; end; function soucet_pod_hl_diag(a:Tpole;N:integer):integer; var i,j,s:integer; begin s:=0; for I:=1 to n do for j:= 1 to N do if I > J then s:=s+a[i,j]; soucet_pod_hl_diag:=s; end; begin clrscr; write('zadej rad matice: '); readln(n); napln(pole,N); vytiskni(pole,N); vysl:=soucet_pod_hl_diag(pole,N); writeln('soucet prvku pod hl.diagonalou: ',vysl); write('zmackni ENTER: '); readln; end. 51 Praktická cvičení algoritmů Příklad 2-30 Naplňte dvojrozměrné pole o N řádcích a M sloupcích náhodnými čísly a vytiskněte ve tvaru matice. Zjistěte a vytiskněte maximální hodnotu v každém řádku pole. Rozbor řešení: Algoritmus nalezení extrémní hodnoty je vysvětlen v PR1_35 a jeho aplikace je v PR2_23. Funkce pro vyhledání maximální hodnoty pracuje s jednorozměrným polem, proto v okamžiku, kdy ji potřebujeme zavolat, je třeba konkrétní řádek matice přepsat do jednorozměrného pole a to pak uvést jako skutečný parametr funkce. Řešení v Borland Pascalu: program PR2_30; uses crt; type Tpole = array[1..20,1..20] of integer; Tradek = array[1..20] of integer; var pole:Tpole; rad:Tradek; vysl,N,I,J:integer; procedure napln(var a:Tpole;N:integer); var i,j:integer; begin randomize; for i:= 1 to N do for j:=1 to N do a[i,j]:=random(10)+1; end; procedure vytiskni(a:Tpole;N:integer); var i,j:integer; begin for i:= 1 to N do begin for j:=1 to N do write(a[i,j]:3); writeln; end; writeln; end; function maximum(a:Tradek;N:integer):integer; var i,max:integer; begin max:=0; for I:=1 to n do if A[I] > max then max:=A[I]; maximum:=max; end; 52 Praktická cvičení algoritmů begin clrscr; write('zadej rad matice: '); readln(n); napln(pole,N); vytiskni(pole,N); Writeln; for I:=1 to N do begin for J:= 1 to N do rad[J]:=pole[I,j]; vysl:= maximum(rad,N); writeln('Max.hodnota ',I,'.radku: ',vysl); end; writeln; write('zmackni ENTER: '); readln; end. Samostatný úkol č. 8 Naplňte dvojrozměrné pole o N řádcích a M sloupcích náhodnými čísly a vytiskněte ve tvaru matice. Sestavte funkci, která zjistí součet prvků, které tvoří obvod pole. 2.3. Záznam Při návrhu algoritmu řešení některých úloh se dostáváme do situace, kdy o jednom objektu si musíme pamatovat informace různých datových typů. Příkladem může být třeba informace o studentovi, kdy potřebujeme znát jeho jméno, data narození, prospěch. Každý s těchto údajů je jiného datového typu, ale patří ke stejnému objektu. A právě v takových případech je velmi vhodné pro uložení dat použít datovou strukturu záznam. Datový typ záznam je strukturovaný heterogenní datový typ. Proměnná typu záznam se skládá z dílčích položek, které mohou být různých datových typů. Příklad 2_31 Vytvořte kartotéku zaměstnanců o maximálním počtu 30 záznamů. O každém zaměstnanci evidujeme : Příjmení Jméno Věk Měsíční plat. Vytvořte komunikační prostředí, které uživateli umožní zvolit, zda chce zadávat dalšího zaměstnance (na základě dotazu a odpovědi). Po ukončení zadávání se automaticky vypíše obsah kartotéky na obrazovku ve formě jakési tabulky (srovnat do sloupců). Pak zjistěte, zda je v kartotéce člověk s příjmením, které načtete z klávesnice a uložíte do proměnné HL_PRIJM. Pokud takové příjmení v kartotéce je, vypište na obrazovku všechny lidí s tímto příjmením. 53 Praktická cvičení algoritmů Rozbor řešení: Definujeme v úseku TYPE vlastní datový typ záznam a typ pole záznamů. Každý prvek pole bude tedy mít strukturu odpovídající položkám záznamu. K prvkům pole přistupujeme přes jeden index, ale pak ještě přes tečkovou konvenci přistupujeme k jednotlivým položkám. Řešení v Borland Pascalu: program PR2_31; uses crt; type Tzam = record prijm:string[20]; jmeno:string[10]; vek:byte; plat:longint; end; Tpole = array [1.. 30] of Tzam; var osoba:Tzam; pole:Tpole; Hl_prijm:string[20]; i,odp,pom,p:integer; Function prevod(s:string):string; var i:integer; begin for i:=1 to length(s) do s[I]:=upcase(s[i]); prevod:=s; end; begin clrscr; i:=0; repeat Inc(i); with pole[i] do begin write('zadej prijmeni: '); readln(prijm); write('zadej jmeno: '); readln(jmeno); write('Zadej vek: '); readln(vek); write('Zadej plat: '); readln(plat); end; writeln; repeat write('chces zadat dalsi osobu ? 1= ano 2= Ne readln(odp); until (odp = 1) or (odp = 2); until odp = 2; writeln; writeln(' prijm jmeno vek plat'); for p:=1 to i do with pole[p] do begin writeln(prijm:10,jmeno:10,vek:7,plat:8); end; 54 : '); Praktická cvičení algoritmů writeln; Write('zadej hledane prijmeni:'); readln(hl_prijm); writeln; pom:=0; for p:=1 to i do with pole[p] do begin if prevod(prijm) = prevod(hl_prijm) then begin writeln(prijm:10,jmeno:10,vek:7,plat:8); pom:=1; end; end; if pom=0 then writeln('clovek s hledanym prijmenim neni.'); writeln; write('zmackni ENTER'); readln; end. Průvodce studiem: Existence datové struktury záznam ulehčuje programátorům práci a napomáhá větší přehlednosti zdrojového textu. Využití tohoto datového typu je ve větším rozsahu vysvětleno v kapitole 3.2.Typový soubor. 2.4. Množina Základní charakteristika datové struktury množina: • Všechny položky struktury musí být stejného datového typu, dovolené jsou pouze ordinální datové typy • Položky se ve struktuře nemohou opakovat • Každé položce je přiděleno ordinální číslo z rozsahu 0 .. 255 Obecná definice typu množina v Pascalu: SET OF ordinální datový typ; Základem pro definování typu množina je vždy určitý ordinální datový typ, tzv. bázový typ. Příklad 2-41 Přečtěte znak z klávesnice a určete, zda se jedná o malé nebo velké písmeno, případně zda to vůbec není písmeno. Celý algoritmus se bude opakovat, jako ukončující znak zvolíme hvězdičku. 55 Praktická cvičení algoritmů Rozbor řešení: V programu použijeme proměnnou typu množina, kterou můžeme deklarovat např. takto: var PISMENA set of CHAR; její hodnotu definujeme příkazem : PISMENA := [‘A’ .. ‘Z’]; Řešení v Borland Pascalu: Program PR2_41; uses crt; var velka_pism,mala_pism: set of char; znak:char; begin clrscr; velka_pism:=['A'..'Z']; mala_pism:=['a'..'z']; write('zadej znak * = konec programu: '); readln(znak); writeln; while znak <>'*' do begin if znak in velka_pism then writeln('znak je velke pismeno') else if znak in mala_pism then writeln('znak je male pismeno') else writeln('znak neni pismeno'); writeln; write('zadej znak * = konec programu: '); readln(znak); writeln; end; end. Průvodce studiem: Příklad 2_41 je docela dobře řešitelný bez znalosti datové struktury množina. Stačí, když budete umět pracovat s proměnnou typu CHAR. Zařadila jsem řešení pomocí množiny proto, abyste si vyzkoušeli tento datový typ použít. Hlavně si všimněte operátoru IN, který zjišťuje náležitost prvku k množině. Tento operátor vám umožňuje elegantním způsobem nahradit složité sestavování logických podmínek. Příklad 2-42 Sestavte program, který vygeneruje 6 různých čísel z intervalu 1 .. 49 (čísla do SPORTKY). Využijte datový typ množina. Rozbor řešení: Deklarujeme proměnnou CISLA set of INTEGER; její hodnotu definujeme příkazem: CISLA:= [1 ..49]; V programu pak postupně generujeme čísla. Pokud číslo je v dané množině, uložíme jej do pole a z množiny jej odstraníme (odečteme). 56 Praktická cvičení algoritmů Řešení v Borland Pascalu: Program PR2_42; { program generuje 6 cisel v rozmezi 1 ..49 ukazka reseni s pouzitim datoveho typu mnozina } uses crt; type inter = 1..49; var cis,i,opak:inter; mn_cis:set of inter; pole:array[1..6] of inter; begin randomize; clrscr; repeat mn_cis:=[1..49]; i:=1; repeat cis:=random(49)+1; if cis in mn_cis then begin pole[i]:=cis; mn_cis:=mn_cis-[cis]; i:=i+1; end; until i>=7; write('cisla do sportky: '); for I:=1 to 6 do write(pole[i]:3); writeln;writeln; write('opakovat cely vypocet ? '); writeln('1=ano, ostatni cisla = konec programu'); readln(opak); until opak <> 1; end. Příklad 2-43 Přečtěte větu a zjistěte, kolik slov ve větě má více než 3 samohlásky. Rozbor řešení: Deklarujeme proměnnou var SAMOHL set of CHAR; její hodnotu definujeme příkazem: SAMOHL:= [‘A’ , ‘E’, ‘I’, ‘O’, ‘U’, ‘Y’]; v programu použijeme možnost odčítání dvou množin Řešení v Borland Pascalu: Program PR2_43; uses crt; { program zjisti, kolik slov ve vete ma alespon 3 ruzne samohlasky ukazka reseni s pouzitim datoveho typu mnozina } var s:string; samohl:set of char; i,p,p_slov,opak:integer; begin repeat clrscr; p:=0;p_slov:=0; 57 Praktická cvičení algoritmů repeat writeln('Napis vetu ukoncenou teckou:'); readln(s); if s[length(s)]<>'.' then writeln('veta musi koncit teckou, zadejte znovu'); until s[length(s)]= '.'; samohl:=['A','E','I','O','U']; for I:= 1 to length(s) do begin if upcase(s[I]) in samohl then begin inc(p); samohl:=samohl-[upcase(s[I])]; end else if (s[i] = ' ') or (s[i] = '.') then begin if p>= 3 then inc(p_slov); p:=0; samohl:=['A','E','I','O','U']; end; end; writeln('pocet slov,ktera maji alespon 3 ruzne samohl.:',p_slov); writeln; write('opakovat cely vypocet ? '); writeln('1=ano, ostatni cisla = konec programu'); readln(opak); until opak <> 1; end. Příklad 2-44 Sestavte program, jehož vstupem jsou dvě posloupnosti čísel z intervalu <1,50>. Každá posloupnost obsahuje N hodnot. Z každé posloupnosti vytvořte množinu hodnot. Vytvořte průnik množin. Rozbor řešení: V programu postupně dochází ke generování čísel ze zadaného intervalu. Pro každé vygenerované číslo zjistíme, zda je v množině pomocí operátoru IN. Pokud tam číslo není, tak se do množiny vloží (pomocí operátoru +). Až jsou tímto způsobem naplněny obě množiny, pak potřebujeme zjistit, která čísla množiny tvoří. Prvky množiny ale nelze přímo tisknout, proto je tisk zajištěn pomocí příkazu cyklu s řídící proměnnou. Hodnota této řídící proměnné se mění v intervalu <1,50> , což je interval, ze kterého byla generována čísla. Řešení v Borland Pascalu: Program PR2_44; uses crt; type inter = 1..50; var K,N,I:integer; p:inter; A,B,C:set of inter; begin clrscr; randomize; 58 Praktická cvičení algoritmů Write('Pocet prvku mnoziny A: '); Readln(N); A:=[]; I:=0; while I<N do begin p:=random(50)+1; if NOT (p IN A) then begin A:= A+[p]; INC(I); end; end; write('mnozina A obsahuje tyto prvky:'); for p:= 1 to 50 do if p IN A then write(p,','); writeln;writeln; Write('Pocet prvku mnoziny B: '); Readln(N); B:=[]; I:=0; while I<N do begin p:=random(50)+1; if NOT (p IN B) then begin B:= B+[p]; INC(I); end; end; write('mnozina B obsahuje tyto prvky:'); for p:= 1 to 50 do if p IN B then write(p,','); writeln;writeln; C := A*B; If C = [] then writeln('Prunik A a B je prazdna mnozina ') else begin write('Prunik mnozin A a B je mnozina: ['); for p:= 1 to 50 do if p IN C then write(p,','); writeln(#8,']'); end; writeln; writeln('zmackni ENTER'); readln end. Samostatný úkol č.9 Program PR2_44 rozšiřte o ostatní množinové operace. 2.5. Korespondenční úkol č. 3 Zadání: Vyberte si jeden z úkolů určených pro samostatnou práci uvedených v kapitole 2 a pošlete jej ke kontrole. 59 Praktická cvičení algoritmů Kontrolní otázky: 1. 2. 3. 4. 5. 6. 7. 8. Co je to datová struktura ? Jak budete deklarovat proměnnou, do které chcete uložit textový řetězec ? Co je to nultý byte v řetězci ? Který datový typ použijete, jestliže potřebuje v paměti počítače uložit více dat stejného typu ? Který datový typ odpovídá matematickému pojmu vektor ? Který datový typ odpovídá matematickému pojmu matice ? Který datový typ použijete, jestliže potřebuje v paměti počítače uložit více dat různého typu ? Co platí pro proměnnou typu množina ? 3. Uložení dat mimo operační paměť Cíl: Po nastudování této části textu byste měli umět: - vytvořit soubor dat mimo operační paměť počítače - uložit data do externího souboru - uložit data z externího souboru do operační paměti Klíčová slova: Operační paměť, soubor, datový typ soubor, proměnná typu soubor, textový soubor, typový soubor, sekvenční přístup, přímý přístup, otevření souboru, zavření souboru. Programovací jazyk využívá pro uložení dat a jejich následné zpracování přednostně operační paměť počítače. Pokud ale nastane situaci, že se data do paměti nevejdou, lze data uložit mimo operační paměť. K uložení dat mimo operační paměť slouží v Borland Pascalu datový typ soubor. Veškeré podprogramy, které jsou v následujících programech použity, je možné vyhledat ve vestavěné nápovědě programovacího jazyka. 3.1. Textový soubor Textový soubor budeme vytvářet tehdy, pokud potřebujeme mimo operační paměť uložit textové údaje. Textový soubor je pro programátory čitelný, je možné se na něj podívat v nějakém textovém editoru a také přímo v integrovaném prostředí Borland Pascalu, kde také můžeme tento soubor editovat. 60 Praktická cvičení algoritmů Příklad 3-1 Vytvořte textový soubor TEXT.TXT v pracovním adresáři, zapište do něj 5 vět. Vytvořte kopii tohoto textového souboru (KOPIE.TXT). Oba soubory vypište na obrazovku. Rozbor řešení: Protože vytváříme nový soubor, musíme jej otevřít v režimu REWRITE. Každou větu nejdříve načteme do operační paměti a pak ji teprve uložíme externího souboru. Po skončení ukládání je nutné soubor uzavřít a pak znovu otevřít pro čtení. Přesný popis významu použitých podprogramů vyhledejte v nápovědě. Řešení v Borland Pascalu: Program PR3_1; uses crt; var f,k:text; s:string; i,poc:integer; begin clrscr; assign(f,'text.txt'); assign(k,'kopie.txt'); rewrite(f); write('zadej pocet vet :'); readln(poc); writeln; writeln('********** napis ',poc,' vet **********'); writeln; for i:=1 to poc do begin write('zadej ',I,'. vetu : '); readln(S); writeln(F,S); end; close(f); reset(f); writeln; writeln('Obsah puvodniho souboru TEXT.TXT: '); writeln; while not eof(f) do begin readln(f,s); writeln(s); end; close(f); reset(f); rewrite(k); while not eof(f) do begin readln(f,s); writeln(k,s); end; close(f); close(k); reset(k); writeln; 61 Praktická cvičení algoritmů writeln('obsah souboru kopie.txt: '); writeln; while not eof(k) do begin readln(k,s); writeln(s); end; write('zmackni ENTER'); readln; end. Příklad 3-2 Do existujícího souboru TEXT.TXT přidejte další 3 věty. Obsah souboru vytiskněte na obrazovku tak, že před každou větou napište její pořadové číslo. Rozbor řešení: V textovém souboru nejsou jednotlivé položky číslovány. Proto zavedeme počítadlo, jehož hodnotu vždy před tiskem věty zvýšíme o jedničku. Řešení v Borland Pascalu: program PR3_2; uses crt; var f:text; veta:string; i,n:integer; begin clrscr; assign(f,'text.txt'); reset(f); i:=0; Writeln('obsah souboru:'); while not eof(f) do begin inc(i); readln(f,veta); writeln(i,'. ',veta); end; append(f); writeln('zadej vety, ktere se do souboru maji pridat:'); for i:=1 to 3 do begin write('Zadej ',i,'. vetu: '); readln(veta); writeln(f,veta); end; close(f); reset(f); i:=0; while not eof(f) do begin inc(i); readln(f,veta); writeln(i,'. ',veta); end; close(f); writeln('Konec programu. Stiskni ENTER'); readln; end. 62 Praktická cvičení algoritmů Příklad 3-3 Určete četnost výskytu velkých písmen v souboru TEXT.TXT. Rozbor řešení: Četnost výskytu písmen znamená, kolikrát se písmeno vyskytlo. V řešení je pro uložení četnosti výskytu písmen připraveno pole CETNOST, které je indexováno datovým typem CHAR, ale jednotlivé prvky tohoto pole jsou celočíselné. Podle hodnoty znaku se pak zvyšuje o jedničku hodnota toho prvku pole CETNOST, který má index shodný s právě zpracovávaným znakem. Řešení v Borland Pascalu: Program PR3_3; {cetnost vyskytu velkych pismen v textovem souboru} uses crt; var cetnost:array['A'..'Z'] of integer; veta:string; delka,i:integer; zn:char; f:text; Procedure vypis_souboru(var f:text); var v:string; begin reset(f); while not eof(f) do begin readln(f,v); writeln(v); end; writeln; close(f); end; begin clrscr; assign(f,'text.txt'); writeln('obsah souboru text.txt'); writeln; vypis_souboru(f); reset(f); for zn:='A' to 'Z' do cetnost[zn]:= 0; while not eof(f) do begin readln(f,veta); delka:=length(veta); for i:=1 to delka do if veta[i] in ['A'..'Z'] then cetnost[veta[i]]:= cetnost[veta[i]]+1; end; writeln; writeln('tabulka cetnosti vyskytu velkych pismen:'); writeln; 63 Praktická cvičení algoritmů writeln('pismeno pocet vyskytu'); for zn:='A' to 'Z' do if cetnost[zn]>0 then writeln(zn:3,cetnost[zn]:10); writeln; writeln('konec programu, zmackni Enter'); readln; end. Samostatný úkol č. 10 Sestavte program, který uloží obsah textového souboru TEXT.TXT do pole řetězců. Pak pro každou větu zajistěte, aby začínala velkým písmenem, ostatní písmena byla napsána malými písmeny, věta byla ukončena tečkou a mezi jednotlivými slovy byla jen jedna mezera. Po této úpravě zjistěte, kolik nejvíce slov je ve větě. Tuto větu vypište na obrazovku. Všechny věty uložte zpět do původního souboru. 3.2. Typový soubor Typový soubor slouží k uložení dat mimo operační paměť. Při deklaraci proměnné typu soubor stanovujeme, jaká data budeme do souboru ukládat. Soubor patří mezi strukturované homogenní datové typy, proto je možné do jednoho souboru uložit jen jeden typ dat. Typový soubor je soubor s přímým přístupem, tzn. že je možný přímý přístup k jednotlivým položkám. Položky jsou číslovány a v prostředí Borland Pascal jsou podprogramy, které umožňují s tímto souborem dat celkem jednoduše pracovat. Typový soubor je pro programátora nečitelný. Jediný způsob, jak zjistit obsah souboru, je výpis pomocí programu. Příklad 3-21 Do souboru CISLA.DAT zapište N náhodně vygenerovaných čísel z intervalu (1,100). Obsah souboru vypište na obrazovku. Pak do souboru přidejte 5 dalších čísel a opět souboru vypište. Pro výpis souboru sestavte vlastní proceduru. Rozbor řešení: V tomto programu jsou zachyceny základní kroky, které je třeba dodržet při práci s typovým souborem. Význam jednotlivých podprogramů je vhodné dohledat v nápovědě jazyka Borland Pascal. Řešení v Borland Pascalu: program PR3_21; uses crt; const n =10; type Tsoubor = file of integer; var f:Tsoubor; i,r:integer; 64 Praktická cvičení algoritmů procedure vypis(var f:Tsoubor); var r:integer; begin reset(f); while not eof(f) do begin read(f,r); write(r:4); end; writeln; end; begin clrscr; randomize; writeln; assign(f,'cisla.dat'); rewrite(f); for I:= 1 to n do begin r:=random(100); write(f,r); end; reset(f); writeln('obsah souboru: '); vypis (f); writeln; writeln('zadej 5 cisel, ktera chces pridat do souboru:'); for I:= 1 to 5 do begin Write (i,'. cislo: '); readln(r); write(f,r); end; seek(f,0); writeln; writeln('novy obsah souboru:'); vypis(f); close(f); writeln; write('zmackni ENTER'); readln; end. Příklad 3-22 Ze souboru CISLA.DAT vytvořte soubor NOVY.DAT, který bude obsahovat každé třetí číslo z původního souboru. Rozbor řešení: Každá položka v typovém souboru má své číslo, číslování začíná od nuly. Pro zajištění výpisu každé třetí položky je pro zjištění dělitelnosti čísla položky číslem 3 zařazen operátor MOD, který zjišťuje zbytek po dělení. 65 Praktická cvičení algoritmů Řešení v Borland Pascalu: {$I-} program PR3_22; {program cte existujici soubor, do noveho souboru zapisuje kazde treti cislo} uses crt; type Tsoubor = file of integer; var f,f1:Tsoubor; r,p:integer; Iochyba:boolean; procedure vypis(var f:Tsoubor); var r:integer; begin reset(f); while not eof(f) do begin read(f,r); write(r:5); end; writeln; end; begin clrscr; assign(f,'cisla.dat'); assign(f1,'novy.dat'); reset(f); rewrite(f1); Iochyba := Ioresult <> 0; if Iochyba then writeln(#7,'chyba pri otevreni souboru') else begin writeln('puvodni soubor:'); vypis(f); seek(f,0); while not eof(f) do begin read(f,r); p:=filepos(f); if p mod 3 = 0 then write(f1,r); end; seek(f1,0); writeln('novy soubor:'); vypis(f1); close(f); close(f1); end; writeln; write('zmackni ENTER'); readln; {$I+} end. 66 Praktická cvičení algoritmů Průvodce studiem: Ve zdrojovém textu programu je vypnuta automatická kontrola vstupně výstupních operací (V/V). Kontrola se vypíná pomocí direktivy překladače ve tvaru {$I-}, pak překladač kontrolu V/V operací neprovádí a správnost V/V operací kontroluje programátor. Pokud funkce IOResult vrací nulu, byla V/V operace provedena správně. Které hodnoty funkce IOResult může vracet a co to znamená, naleznete v kontextové nápovědě Borland Pascalu. Příklad 3-23 Sestavte program, který umožní vést evidenci osob. O jednotlivých osobách evidujeme Příjmení, Jméno, Věk. Informace zapisujte do souboru EVID.DAT. Po zápisu do souboru se program bude ptát, zda chceme zadávat další osobu. Pokud si vybereme odpověď "ne" , zobrazí se dotaz, zda chceme obsah souboru vypsat na obrazovku před ukončením programu. Rozbor řešení: Soubor, do kterého budeme informace zapisovat, musí mít jednotlivé položky stejného datové typu, jako paměťová proměnná, do které se ukládají data načtena z klávesnice. Při čtení ze souboru jsou jednotlivé položky souboru ukládány do pole záznamů. Řešení v Borland Pascalu: program PR3_23; uses crt; type tosoba = record prijmeni:string; jmeno:string; vek:integer; end; type tpole = array[1..50] of tosoba; var f:file of tosoba; os:tosoba; pole:tpole; i,odp,o:integer; begin clrscr; assign(f,'evid.dat'); rewrite(f); repeat with os do begin write('zadej prijmeni: '); readln(prijmeni); write('zadej jmeno: '); readln(jmeno); write('Zadej vek: '); readln(vek); write(f,os); end; writeln; repeat 67 Praktická cvičení algoritmů write('chces zadat dalsi osobu ? 1= ano 2= Ne : '); readln(odp); until (odp = 1) or (odp = 2); until odp = 2; writeln; Writeln('chces vypsat obsah souboru ? 1 =ano 2 = ne :'); readln(o); if o = 1 then begin reset(f); i:=0; while not eof(f) do begin i:=i+1; read(f,pole[i]); end; for I:=1 to filesize(f) do writeln(pole[i].jmeno:15,pole[i].prijmeni:20,pole[i].vek:10); close(f); end; writeln; write('zmackni ENTER'); readln; end. Příklad 3-24 Sestavte program, který na konec existujícího souboru EVID.DAT přidá informace o třech dalších lidech. Pak vypíše informace o člověkovi, který je nejstarší. Rozbor řešení: V programu pracujeme s existujícím souborem, proto je důležité tento soubor otevřít pomocí procedury RESET a ne REWRITE (došlo by k vynulování souboru). Obsah souboru pak uložíme do paměťového pole záznamů a vyhledání informací o nejstarším člověku se pak provede v tomto poli. Jde pak o nám již známý algoritmus vyhledání extrémní hodnoty a zapamatování si místa jejího výskytu. Řešení v Borland Pascalu: program PR3_24; {vypis existujiciho souboru, nalezeni nejstarsi osoby} uses crt; type tosoba = record prijmeni:string; jmeno:string; vek:integer; end; type tpole = array[1..50] of tosoba; var f:file of tosoba; os:tosoba; pole:tpole; i,p,nej:integer; 68 Praktická cvičení algoritmů begin clrscr; i:=0; assign(f,'evid.dat'); reset(f); seek(f,filesize(f)); Writeln('zadejte informace o 3 novych osobach do souboru'); for I:= 1 to 3 do with os do begin write('zadej prijmeni: '); readln(prijmeni); write('zadej jmeno: '); readln(jmeno); write('Zadej vek: '); readln(vek); write(f,os); end; close(f); reset(f); i:=0; writeln('Obsah souboru:'); while not eof(f) do begin inc(i); read(f,pole[i]); end; writeln(' jmeno prijmeni vek'); writeln; for i:=1 to filesize(f) do WITH pole pole[i] do begin writeln(jmeno:15,prijmeni:20,.vek:10); if nej < vek then begin nej:=vek; p:=i; end; end; writeln; writeln('Nejstarsi osoba:'); writeln(pole[p].jmeno:15,pole[p].prijmeni:20,pole[p].vek:10); writeln; close(f); writeln('Konec programu. Stiskni ENTER.'); readln; end. Samostatný úkol č. 11 Program PR2_31 upravte tak, aby se kartotéka lidí ukládala do souboru Lide.dat. 69 Praktická cvičení algoritmů 3.3. Korespondenční úkol č.4 Zadání: Sestavte program, který vypíše na obrazovku obsah existujícího souboru EVID.DAT a zároveň uloží informace do pole záznamů. Pak zjistí, jaký je průměrný věk osob zapsaných v souboru. Na obrazovku vypíše informaci o průměrném věku a také informace o všech osobách, které jsou starší než je zjištěný průměrný věk. Kontrolní otázky: 1. Jak je možné v prostředí Borland Pascalu uložit data mimo operační paměť ? 2. Do které skupiny patří datový typ soubor ? 3. Které typy souborů dat je možné v Borland Pascalu vytvořit ? 4. Jaký je rozdíl mezi textovým a typovým souborem dat ? 5. Které režimy otevření souboru jsou zavedeny pro práci s textovým souborem ? 6. Které režimy otevření souboru jsou zavedeny pro práci s typovým souborem ? 7. Který se souborů je soubor s přímým přístupem a co to znamená? 8. Co znamená soubor se sekvenčním přístupem ? 4. Závěr Předložený text by vám měl pomoci v lepší orientaci při sestavování programů v programovacím jazyku Borland Pascal. Pokud vás při studium toho kterého algoritmu napadne řešení jiné, je to naprosto v pořádku. Zde uvedená řešení jsou obvykle jednou z několika možných variant řešení a měla by vám být návodem, jak postupovat při řešení konkrétních úkolů z praxe. Prostudování uvedených zdrojových textů vám usnadní práci při analýze a návrhu složitějších algoritmů, protože již víte jaké možností vám programovací jazyk Borland Pascal nabízí při ukládání dat v operační paměti i mimo operační paměť, vyzkoušeli jste si principy dalšího zpracování dat v závislosti na typu uložených dat. Algoritmy, se kterými jste se zde seznámili, vám mohou pomoci i v případech, když se dostanete do situace, ve které vámi zvolené řešení se projeví jako neefektivní nebo chybné. Nechat se inspirovat jiným pohledem na řešený problém není chybou, může to být naopak velmi prospěšné. Tento studijní text s programy sestavenými v jazyku Borland Pascal můžete využít i při tvorbě programů v jiném programovacím jazyku, protože rozbor řešení u každého zde vloženého příkladu vám postačí k pochopení algoritmu a zápis v jiném programovacím jazyku je pak jen otázkou jiné syntaxe. 70 Praktická cvičení algoritmů 5. Literatura Základní literatura Satrapa, P.: Pascal pro zelenáče, Neokortex, 2000 Kvoch, M.: Programování v TURBO PASCALU 7.0, KOPP, 1994 Töpfer, P.: Programovací techniky, Prometheus, 1995 Doporučená literatura Kvoch, M.:Sbírka úloh z jazyka Pascal, KOPP, 1993 Drozd, J., Kryl, M.: Začínáme s programováním, GRADA, 1992 Ježowicz, E., Laga, J.: Základy programování v PASCALU, Státní pedagogické nakladatelství, 1989 Mikula, P.: TURBO PASCAL 7.0 od příkladů k příkazům, GRADA 1993 Vogel, J.,Müller, K.,Jinoch, J.: Programování v jazyku PASCAL, SNTL 1988 71
Podobné dokumenty
Práce s daty 2 - Starší publikace a seriály
Šířka výstupního pole........................................................................................................ 97
Zobrazení znaménka u kladných čísel ...................................
Modulární systém kurzů programovacích nástrojů Borland
jsou přiloženy jejich zdrojové soubory s příponou PAS a přímo spustitelné programy
s příponou EXE.
Výuka lze rozdělit do dvou částí. V první části si žáci osvojí a naučí používání
základních strukt...
Upravená učebnice C a C++
Vlastní program, tedy náš seznam úkolů pro počítač, je mezi znaménky { a } (složené závorky). To co zbylo
před našimi závorkami - main() - si také vysvětlíme v některém pozdějším díle (pro dnešek s...
Programování v jazyku C-C++
programu lišit, neboť paměť přiřazuje programu operační systém.
V programu jsou chyby. Přímé přiřazení hodnoty do proměnné uf (uf = 50;) a pak v poslední funkci printf,
kde se tiskne hodnota ukazat...
Průvodce světem králíka Petra
Každý z datových prvků při vytváření programu něco obsahuje. Číselná proměnná
obsahuje číslo, obrázková proměnná obrázek, předmětová proměnná předmět (což je
v podstatě obrázek o velikosti 32x32 b...
1. DÚ
zadávat různé vstupy (po rozkliknutı́ odkazu hned pod oknem na zdrojový
kód) do pole input, v poli output se objevı́ výsledek. (Pokud vaše programy
nepoběžı́ bleskově rychle, je vhodné m...
ZÁKLADY PROGRAMOVACÍHO JAZYKA TURBO PASCAL
- přečtením hodnoty do proměnné (ze vstupu, příkaz read nebo readln)
- dosazením hodnoty do proměnné – přiřazovací příkaz, např. A:=7 (dosazení sedmičky do proměnné A)
Aritmetické operátory a aritm...