Prezentace se nahrává, počkejte prosím

Prezentace se nahrává, počkejte prosím

AII = Algoritmy pro inženýrskou informatiku Přednášky.

Podobné prezentace


Prezentace na téma: "AII = Algoritmy pro inženýrskou informatiku Přednášky."— Transkript prezentace:

1 AII = Algoritmy pro inženýrskou informatiku Přednášky

2 Členění přednášek první 2-3 přednášky: úplné základy na téma „jak počítá počítač“ další cca 4 přednášky: základy jazyka Pascal (Delphi) celý zbytek semestru: algoritmy

3 Literatura první téma je popsáno v mnoha menších článcích; souhrnné zpracování neexistuje k výuce Pascalu je vhodná jakákoliv učebnice, na kterou narazíte (ve skriptárně jich je průběžně několik pro FEL, ale vhodné jsou i stredoškolské učebnice apod.).

4 …pokračování... Pro algoritmy je předepsaná učebnice Wirth, Niklaus: Algoritmy a struktúry údajov, ALFA, Bratislava 1988,9. Ideální, ale drahá a anglická je Cormen, Leiserson, Rivest, Stein: Introduction to Algorithms (je to standardní učebnice US vysokých škol, existuje asi ve 30 různých vydáních).

5 …pokračování Jako učebnice Delphi jsou vynikající knihy od Marko Cantú: Mastering Delphi 1...7, jejich české překlady jsou Mistrovství v Delphi 1..4, resp. Myslíme v Delphi Pozor na Mistrovství v Delphi 5; pod tímto názvem vyšlo v češtině něco jiného (není to moc dobré).

6 Požadavky Program přednášek, cvičení, otázky ke zkoušce a další informace jsou na Podmínky pro udělení zápočtu AII: Úspěšné složení nadpoloviční většiny testů, které se budou psát v rámci cvičení z AII.

7 …pokračování Složení zkoušky z AII: Zkouška má teoretickou a praktickou část, tou druhou začíná v učebně 308. Každý student dostane za úkol vytvořit program, na jeho odladění má jednu až cca pět hodin času. Až se mu to povede, může jej obhájit u ústní části. Navazující teoretická část vychází ze zkouškových otázek z tohoto předmětu.

8 Základní funkční bloky počítače AB DB μP PC=čítač instrukcí dekodér instrukcí mnoho dalších registrů střadač (akumulátor) PSW, SP,... R/ W ALU Periférie RAM,ROM 100:37 101:02 102: :56

9 …pokračování Základní funkční bloky PC jsou mikroprocesor (μP), paměti (RAM, ROM), periferní zařízení (diskové jednotky, …) navzájem jsou propojeny 3 sběrnicemi: –řídicí (zde nakreslen jen vodič R/Ŵ) –adresová –datová

10 Adresová sběrnice Jednosměrná: µP -> periférie šířka 32 bitů: adresovací prostor 2 32 starší mikroprocesory: šířka jen 20 (24) bitů, adresovací prostor 2 20 = 1MB, zcela nedostačující, nutnost stránkování

11 Datová sběrnice Obousměrná: µP -> periférie nebo periférie -> µP o směru toku dat rozhoduje vodič R/Ŵ z řídicí sběrnice směr (čtení/zápis) se vždy posuzuje z hlediska mikroprocesoru

12 Mikroprocesor Obsahuje mnoho specializovaných pamětí (= registrů) pro nás jsou důležité střadač (=akumulátor), čítač instrukcí PC, ukazatel zásobníku SP, stavový registr PSW ALU = aritmeticko-logická jednotka, provádí výpočty (výsledek vesměs ukládá do střadače)

13 Instrukční cyklus Popis instrukčního cyklu zahájíme tím, že čítač instrukcí (PC) obsahuje adresu instrukce, která se má provést. Tudíž ji je potřeba nejprve načíst do µP (tzv. Fetch) obsah PC se po adresové sběrnici rozšíří do všech periférních zařízení ale adresa je jedinečná, proto jen 1 zařízení na ni může reagovat

14 …pokračování... Představme si, že adresa je 100 na adresu 100 zareaguje paměť ROM. Zjistí, že na adrese 100 je hodnota 37. Protože vodič R/Ŵ je ve stavu Read, tak paměť hodnotu 37 přiloží na datovou sběrnici. hodnota z paměti se rozšíří po celém počítači, tedy i do µP

15 …pokračování... Uvnitř µP se hodnota načte do dekodéru instrukcí. dekodér instrukcí má za úkol rozšifrovat, o jakou se jedná instrukci a podle toho zařídit její provedení po vykonání instrukce čítne PC o +1 takže na adresové sběrnici se objeví další adresa (101) a celý proces se opakuje

16 …pokračování... Většina instrukcí není tak jednoduchých Kdyby se například jednalo o instrukci „přičti číslo, které následuje, k obsahu střadače“, muselo by se nejdříve načíst to „číslo, které následuje“. To znamená, že instrukční cyklus by pokračoval: PC by na adresovou sběrnici přiložil adresu 101,

17 …pokračování... paměť by na datovou sběrnici přiložila obsah adresy 101, v našem případě číslo 02 číslo 02 by se načetlo do µP. Tentokrát by se ale hodnota nenačetla do dekodéru instrukcí, ale přes přepnutý vnitřní přepínač by se přivedla do ALU ALU provede výpočet. Výsledek většinou zůstane ve střadači

18 …pokračování Kdyby šlo o instrukci, která má data zapsat do paměti, průběh by byl obdobný až na to, že vodičem R/Ŵ by se namísto čtení z adresy 101 vyvolal zápis do adresy 101 Tedy: program je posloupnost čísel, která mohou znamenat jak data, tak instrukce - rozhoduje o tom pořadí (první je vždycky instrukční kód)

19 Nepodmíněný skok Popsali jsme si průběh programu, který je zcela lineární. Takový program by nebyl k velkému užitku (nepodmíněné) skoky na jinou adresu se provádějí tak, že se do PC vloží cílová adresa skoku (často načtená z paměti, nebo vypočtená) program pak pokračuje od jiné adresy

20 Podmíněný skok Důležité jsou skoky podmíněné - provádějí se jen tehdy, když nastaly určité podmínky µP obsahuje sadu jednobitových pamětí, sestavených do registru PSW. Po každé instrukci se v PSW nastaví bity podle výsledku instrukce (např. jestli je výsledek nula, záporný, došlo k přetečení apod.) Podmíněné skoky se provedou, když je v PSW správná hodnota

21 Volání procedury Často potřebujeme opakovat nějakou část programu - např. výpočet nějaké funkce Jednoduché: na začátek funkce by šlo skočit z mnoha míst Ale problém: jak zajistit, aby skok na konci funkce směřoval, kam má?! hlavní program funkce

22 …pokračování... nápad: při skoku na podprogram si poznamenat, odkud se skok prováděl nevýhoda: kdyby si µP poznamenával návratové adresy do registrů, mohl by realizovat jen tolik úrovní podprogramů, kolik by měl registrů  řešení: v paměti RAM vytvořit zásobník (stack), velikost podle potřeby

23 …pokračování... registr SP (=Stack Pointer) bude vždy ukazovat na vrchol zásobníku na začátku např. SP=2000 zavedeme novou instrukci, volání podprogramu (Call) STACK 2000: 2001: 2002:... SP 2000

24 …pokračování... Kdyby v hlavním programu na adrese 200 bylo volání funkce začínající na 900, tak se: –návratová adresa (v našem případě 201) zapíše do zásobníku tam, kam ukazuje SP (to znamená na adresu 2000) –posune SP tak, aby zase ukazoval na volné místo (tedy na 2001) –provede skok na podprogram (v našem příkladu na adresu 900) viz následující obrázek

25 …pokračování... STACK 2000: : 2002:... SP 2001

26 …pokračování... Kdyby uvnitř funkce (třeba na adrese 955) bylo další volání (jiného) podprogramu, volání by proběhlo zcela obdobně viz následující obrázek

27 …pokračování STACK 2000: : :... SP 2002

28 Návrat z podprogramu Na konci podprogramu musí být zvláštní instrukce RET = návrat z podprogramu Operace se zásobníkem se dělají inverzně k volání, to znamená: STACK 2000: : :... SP 2001

29 …pokračování... SP se vrátí na předcházející pozici (je tam číslo 956) obsah té pozice (číslo 956) se načte a použije se jako adresa, na kterou se skočí. Takže skok na adresu 956 znamená, že jsme se vrátili těsně za poslední volání podprogramu

30 …pokračování až na konci podprogramu (980) najdeme RET, postup se opakuje takže nastane návrat na adresu 201, tedy za volání funkce. STACK 2000: : :... SP 2000

31 Teorie a praxe Popsaný postup byl jen schéma; realita se dost liší: –zásobník (stack) roste k menším číslům, t.zn. narůstá nahoru, –návratové adresy mají více bytů, a proto zásobník roste/klesá po více bytech, –na zásobník se ukládají i četné jiné údaje.

32 Přerušení Typickým příkladem, kdy se pracuje s přerušením, je obsluha pomalých zařízení (tiskárna) přerušení funguje podobně jako volání procedury; vyvolává se však hardwarově na rozdíl od CALL je přerušení asynchronní, může nastat kdykoliv  musíme se postarat o PSW  na zásobník se ukládá i PSW  pro návrat musí být zvláštní instrukce IRET

33 Architektura von Neumannova Popsaná architektura počítače se nazývá von Neumannova další klasická architektura je harvardská (má oddělenou sběrnici pro přenos dat od sběrnice, po které se přenáší program – vyšší rychlost, zejména při zpracování dat) moderní vývoj zaměřen na zvyšování výkonu  hlavně víceprocesorová řešení

34 Víceprocesorové počítače Hlavní problém: i malé množství sekvenčních instrukcí dokáže výrazně zpomalit výpočty Několik koncepcí, např. –tatáž instrukce x N procesorů (operují na různých datech) –maticové procesory (hyperkrychle) –RISC (=Reduced Instruction Set Computer)

35 RISC zjednodušená sada instrukcí: všechny instrukce mají stejný počet taktů (např. 5) současně se vykonává několik (např. 5 instrukcí), ale každá je v jiné fázi důsledek: během každé fáze se dokončí 1 instrukce načtení instrukce načtení operandu výpočet adresy výpočet zápis výsledku

36 Základy programového vybavení Program = posloupnost instrukcí a dat, přičemž rozlišit je lze jen pořadím Strojový kód: čitelný pro stroj (ne pro lidi) Jazyk symbolických adres (assembler): zavedení mnemonik (CALL, JMP ADD…). Převod na instrukce 1:1 Vyšší programovací jazyky (FORTRAN, COBOL, LISP, …) - 1 řádek programu se rozvede na N instrukcí

37 Vyšší programovací jazyky zdrojový text se přeloží do mezijazyku (někdy do assembleru) kompilátor připojí se knihovny sestavovací program (linker) výsledek se dopřeloží do spustitelného kódu (vesměs součást linkeru) zdroje mezikód knihovny program

38 Interpreter Některé jazyky (BASIC, Pascal) lze vedle kompilace také interpretovat. Interpretace probíhá tak, že se program nepřeloží najednou jako celek, nýbrž řádek po řádku se načte  přeloží  vykoná  zapomene. Výhoda: snadné ladění. Nevýhoda: pomalé

39 Krize programování Snaha odstranit omezení, která na programátora kladl programovací jazyk: –generace jazyků umožňujících "skoro všechno" (Fortran 77, PL/1, ADA) –Zklamání: programátor věnuje psaní kódu jen 20% času, pouhých 10% vymýšlení algoritmů a celých 70% ladění !

40 Jazyky pro usnadnění ladění snaha definovat jazyky, které by automatizovaly ladění: –jazyky 3. generace (PASCAL,...) –Problém: tyto jazyky nepodporují spolupráci více programátorů na jednom projektu !

41 …pokračování Jak toho dosáhnout: maximum chyb odhalit už při kompilaci (zejména syntaktické chyby) za chodu programu sledovat podezřelé anomálie, které mohou signalizovat chybu Tímto postupem nelze odhalit algoritmické chyby !

42 PASCAL a snadné ladění Silná typová kontrola –nelze použít nic, co nebylo předem deklarováno –kontrola správnosti typů při přiřazení apod. –kontrola rozsahů a typů za běhu programu „znechucování“ programátorů –typický příklad: GOTO strukturované programování

43 Strukturované programování Programy nelze zkoumat odděleně od dat, nad kterými operují; nutno je chápat v jednotě Programy i data postupně dekomponovat na jednodušší části Důsledek: pro programy používat strukturované příkazy

44 Strukturované příkazy Strukturovaný příkaz má 1 vstup a 1 výstup Důsledek: příkazy a skupiny příkazů lze do sebe hierarchicky vnořovat Nestrukturované příkazy (jako je IF) nutno upravit

45 Další vývoj? Objektově orientované programování (viz předmět OOP) Deklarativní programování (neříkáme počítači, jak to má udělat, ale jen co se má udělat) Grafické programovací rozhraní, programování příkladem, umělá inteligence...

46 Lokální sítě Omezení na pozemek jednoho majitele (právní důvody) Celková délka kabelů do stovek metrů Přenosová rychlost (teoreticky) 10, 100 nebo 1000 Mbit/s Médium: optické kabely, metalické kabely (dříve koaxiální, dnes téměř výlučně symetrický kabelový pár = twist)

47 Síť s kruhovou topologií stanice A stanice D stanice B stanice C monitorovací uzel

48 …pokračování... Když stanice A chce poslat zprávu stanici C, rozdělí ji do rámců (paketů): adresa odesílatele adresa příjemce služební údaje zabezpečení DATA

49 …pokračování... Existují 2 technologie: buď existuje konstantní počet rámců (shodný s počtem stanic), nebo se rámce vkládají stanice A odešle svůj rámec po směru sítě, tzn. stanici B. ta zjistí, že rámec není určen pro ni  beze změny ho pošle dál

50 …pokračování... Až stanice C zjistí, že rámec je její a načte si její obsah ve služebních údajích nastaví bit, že si zprávu načetla. Pak paket pošle dál až paket dorazí zpátky k odesílateli A, je to pro něj potvrzením o správném doručení odesílatel A paket stáhne z oběhu

51 …pokračování... Problém: co když vlivem nějaké poruchy dojde k poškození adresy odesílatele? vznikne bludný paket, který bloudí sítí věčně, zdržuje a nikdo ho nezruší proti bludným paketům se zavede monitorovací uzel, který si každý paket při průchodu označí. Jakmile nějaký paket projde monitorovacím uzlem 2x, je bludný a je vyřazen.

52 …pokračování... Typické pro optická vlákna (protože jsou jednosměrná) známý obchodní produkt: Token Ring ® (IBM) největší nevýhoda: citlivost na poruchy (možnost zlepšit zdvojením, viz následující obrázek)

53 Dvojitá kruhová síť stanice D stanice B stanice C stanice A

54 …pokračování... Když v některé sekci dojde k poruše, přilehlé stanice automaticky sekci vyřadí a síť je schopna pokračovat v práci viz obrázek na následující straně

55 …pokračování stanice D stanice B stanice C stanice A

56 Topologie sběrnice/hvězda Typické pro sítě s protokolem CSMA/CD (Carrier Sense, Multiple Access/Collision Detection): Mnohonásobný náhodný přístup s detekcí nosné a řešením kolize Obchodní produkt: Ethernet ® stanice Astanice Bstanice Z

57 CSMA/CD Zajímavost: poprvé navržena pro rádiovou síť Aloha (universita na Havaji) přenos probíhá v paketech síť nemá žádný řídicí člen (supervizora), což je důležité z hlediska spolehlivosti (vojenství)

58 …pokračování... Když stanice A chce odeslat paket, nejprve si poslechne, jestli na lince neběží nějaký přenos (= detekce nosné) pokud zjistí cizí přenos, počká náhodnou dobu a pokusí se znovu pokud nezjistí žádný přenos, začne vysílat svůj paket (= mnohonásobný náhodný přístup)

59 …pokračování... kdyby se stalo, že několik stanic začne vysílat současně, jde o kolizi. jak k ní může dojít? Vlivem konečné rychlosti šíření signálu! To je také jeden z důvodů, proč je omezena fyzická délka kabelů. stanice kolizi zjistí tak, že současně s vysíláním poslouchá a kontroluje, jestli přijala totéž, co vyslala

60 …pokračování... při zjištění kolize okamžitě přestane vysílat (resp. ještě odešle signál JAM), počká náhodnou dobu a pokusí se o odeslání paketu později dojde-li k poškození paketu přenosem, zjistí se to pomocí CRC a paket se zahodí o to, aby došly všechny pakety (a ve správném pořadí), se starají vyšší vrstvy podle modelu ISO/OSI

61 Zahlcení sítě Když budeme zvyšovat požadavky na přenos, roste pravděpodobnost kolize. Protože při kolizi se nic nepřeneslo, každá kolize současně zvyšuje požadavky na přenos! V krajním případě může dojít k zahlcení sítě, při kterém se už pro samé kolize nepřenese vůbec nic!

62 …pokračování... požadavky na přenos skutečně přenesené pakety teorie bod zahlcení

63 …pokračování Proti zahlcení se bojuje tím, že časy, po které stanice při kolizi čeká, jsou sice náhodné - ale při rostoucí hustotě kolizí se protahují. Tak se sice zhoršuje propustnost sítě, ale nikdy nedojde k zablokování (modrá křivka)

64 PASCAL - základy syntaxe slova se oddělují mezerou, tabulátorem, CR/LF nebo komentářem komentář: závorky { xxx } nebo (* xxx *) nebo od // až do konce řádku kompilátor NEROZLIŠUJE malá/velká písmena (samozřejmě mimo stringy) příkazy se oddělují středníkem

65 Zlaté pravidlo o střednících Kdykoliv jste na rozpacích, zda máte či nemáte napsat středník, použijte ho. Jediné 3 případy, kdy se středník napsat nesmí: –mezi IF…THEN…ELSE –za RECORD (nebo CLASS) –za koncem programu ( je tam END. )

66 Typický „zubatý“ tvar BEGIN WRITE (‘Toto je příklad‘); IF X<10 THEN BEGIN Y := X + 1; WRITE (‘Je menší‘); END;

67 Identifikátory Jakákoliv kombinace písmen a číslic, začínající písmenem (znak __ se bere jako písmeno), ale nelze používat diakritiku rozlišuje se jen prvních 64 znaků doporučeno: používat DLOUHÉ identifikátory a kombinovat malá/velká písmena: PlatZaUcetniObdobi

68 Blok LABEL { deklarace návěští } CONST{deklarace konstant } TYPE{deklarace typů } VAR{deklarace proměnných } PROCEDURE x FUNCTION BEGIN { tělo = výkonná část bloku } END deklarativní procedurální

69 Struktura programu PROGRAM JmenoProgramu; BLOK tečka

70 Procedury a funkce PROCEDURE NazevProcedury (Parametry); FUNCTION NazevFunkce (Parametry) : Typ; BLOK

71 Konstanty Pi= 3.141; DlouheCislo= ; Sestnactkove= $FF08; JedenZnak= JinyZnak= Chr(64); // nebo #$40, #64 Retezec= ‘Toto je text’; JinyRetezec= ‘Řetězec na tři’#13#10 + ‘řádky‘^M‘pod sebou‘;

72 Typy: klasifikace Obecné schéma: IdentifikátorTypu = Specifikace; Typy jsou: –jednoduché –strukturované –pointer

73 Jednoduché typy ordinální typy –INTEGER4 byty, a četné odvozené –CHARASCII, nebo WideChar…UNICODE –BOOLEANjen TRUE nebo FALSE –výčtový typ(Karel, Jan, Martin, Lojza) –interval7..26 reálné typy –REAL8 bytů a četné odvozené

74 Integer

75 Real

76 Příklady jednoduchých typů TDenTydne = (Pondeli, Utery, Streda, Ctvrtek, Patek, Sobota, Nedele); // výčet TPracDen = Pondeli..Patek; // výčet TVelkaPismena = ‘A‘..‘Z‘; // interval

77 Strukturované typy Pole ARRAY [index] OF TypPrvků Záznam (record) RECORD Množina SET OF BázovýTyp Soubor TEXTFILE// textový FILE OF TypPrvků// binární s typy FILE// binární, netypový

78 Příklady polí ARRAY [Pondeli..Patek] of Real; ARRAY [Char] OF ; ARRAY [1..5] OF ARRAY [-6..30] OF ARRAY [1..2] OF Byte; ARRAY [1..5, , 1..2] OF Byte;

79 slouží k práci s texty, a to buď v ASCII nebo v UNICODE mnoho stringových procedur a funkcí prakticky neomezená délka Délka, ReferenceCount a nulový byte stringy v Delphi jsou plně kompatibilní s typem PChar String = zvláštní případ pole AHOJ0??4=délka1=RefC

80 Záznam TZaznam= RECORD A :Real; B, C :Byte; S :String; R :ARRAY [1..5] OF Real; END;

81 Reference na položky záznamu Reference se dělá pomocí „tečkové notace“: nejdříve v části TYPE nadeklarujeme typ (např. typ Tzaznam podle předch. obrázku) pak v části VAR vytvoříme proměnnou toho typu, např. Zaznam : TZaznam;

82 …pokračování pak se lze odvolávat na jednotlivé části záznamu například takto: Zaznam.A…reálné číslo Zaznam.C…byte Zaznam.S…string Zaznam.S [6]…6-tý znak stringu Zaznam.R [3]…3-tí reálné číslo pole R bez mezer kolem tečky !

83 Záznam s variantní částí Začátek je konstantní data od CASE až na konec jsou variantní. Selektor záznamu je nepovinný; není-li uveden, musí se jednotlivé identifikátory lišit tak, aby je kompilátor rozpoznal (pozor, v Delphi se musí lišit vždy!)

84 Příklad záznamu s variantní částí TZv = RECORD Cena : Real; CASE D: TDenTydne OF Pondeli: (A : real; B : Byte); Utery..Nedele: (X : Integer); END;

85 …vysvětlení se selektorem záznamu:bez selektoru záznamu Cena D A B D X A B X volné místo (doplněno podle nejdelší varianty)

86 Množina Smyslem množiny je, zpřístupnit rychlé strojové instrukce AND, OR apod. Bázový typ množiny musí být ordinální Z praktických důvodů smí mít nejvýše 256 prvků (= 32 bytů) Množina se vytváří konstruktorem množiny

87 Příklad práce s množinou TYPETMnozina = SET OF CHAR; VARX, Y: TMnozina; BEGIN X := [‘A‘,‘B‘,‘X‘]; Y := X + [‘Y‘]; // sjednocení X := Y * [‘A‘, ‘Y‘, ‘C‘]; // průnik IF ‘X‘ IN Y ….// je prvkem ?

88 Soubory textový soubor: čitelný pro člověka (číslo je ve formě jednotlivých číslic, jako když se tiskne na tiskárně), ale kvůli nutným konverzím pomalé binární soubor: čísla jsou ve stejném formátu jako v paměti binární soubor je typový (všechny záznamy musejí být stejného typu) nebo netypový (není určeno, jaká data obsahuje) v OOP je zvláštní případ Stream

89 Příklad čtení textového souboru VARF : TextFile; S : string; R : Real; BEGIN AssignFile (F, ‘C:\TEMP\Data.TXT‘); Reset (F); Read (F, R); ReadLn (F, S); ReadLn (F, R, S); CloseFile (F); neuvedeme-li F, čte se z klávesnice stringy se mohou číst jedině pomocí ReadLn

90 Zápis do textového souboru VARF : TextFile; S : string; R : Real; BEGIN AssignFile (F, ‘C:\TEMP\Data.TXT‘); Rewrite (F); // nebo Append (F); Write (F, 3.141); //přípustné formátové specifikace R:7:2 WriteLn (F, R, ‘je výsledek‘); CloseFile (F);

91 Čtení typ. binárního souboru VARF : FILE OF Real; R : Real; BEGIN AssignFile (F, ‘C:\TEMP\Data.TXT‘); Reset (F); Read (F, R); Seek (F, 23); // současně otevře i pro zápis Read (F, R); // ReadLn samozřejmě nelze CloseFile (F);

92 Zápis do typ. binárního souboru VARF : FILE OF Real; R : Real; BEGIN AssignFile (F, ‘C:\TEMP\Data.TXT‘); Rewrite (F); // nebo Append (F); Write (F, 3.141); Seek (F, 23); // současně otevře i pro čtení Write (F, R); // nebo Read (F, R); CloseFile (F);

93 Čtení z netypového souboru VARF : FILE; i, j : Integer; Buf : ARRAY[ ] OF Byte; BEGIN AssignFile (F, ‘C:\Data.TXT‘); Reset (F, 1); i := SizeOf (Buf); BlockRead (F, Buf, i, j); IF i<>j THEN Chyba…. poslední parametr je nepovinný počet bytů na 1 položku

94 Proměnné zapisují se v části VAR obecné schéma: Identifikátor : TypProměnné; teprve zápisem v části VAR se proměnným přiděluje místo v paměti. Mluvíme o statické alokaci paměti. Později uvidíme, že existuje i dynamická alokace.

95 Typované konstanty Chovají se jako proměnné, ve starších verzích do nich šlo i přiřazovat hodnotu (dnes o tom rozhoduje direktiva kompilatoru) Jedinou podstatnou změnou je, že mají definovanou počáteční hodnotu. Poznámka: typované konstanty si každý procvičí individuálně; na cvičení se budou probírat složitější případy!

96 …pokračování... CONST Maximum: word= 26; Pomer: real= ; Adresa: pointer= Addr(X); { musím znát při kompilaci } Zahlavi: string[5]='Sekce'; Pravda: string[9] ='Ano';

97 …pokračování... Typovanými konstantami mohou být též pole a recordy, což je velmi výhodné, protože se do nich jednoduše zadávají počáteční hodnoty. Vektory pole se uzavřou do závorky:

98 …pokračování... TYPE TPole = ARRAY[1..3] OF string[20]; CONST Pole : TPole= ( 'Jedna', 'Dva', 'Tri' ); Digits: ARRAY [1..9] OF Char= ('1','2','3','4','5','6','7','8', '9');

99 …pokračování... Digits: ARRAY [1..9] OF Char { dovolené zjednodušení } = ' ';

100 …pokračování... Pokud je pole vícerozměrné, musí se do závorek uzavřít vektory v každé dimenzi: TYPE TMatrix = ARRAY [0..1, 0..1] OF integer; CONST Matrix: TMatrix = ( (1,2), (3,4) );

101 …pokračování... Podobně (tzn. v závorkách) se dají zadat též položky recordu. U recordů se navíc musí udat též jméno položky (zakončené dvojtečkou) a položky se musí zapsat v přesně stejném pořadí jako byly deklarovány. Příklad:

102 …pokračování... TYPE TBod = RECORD X,Y : real END; CONST Bod : TBod = ( X: 1.23; Y: 9.62 );

103 …pokračování... Podobná pravidla platí i pro množiny, které se konstruují uzavřením do hranatých závorek: TYPE TDig = SET OF 0..9; CONST Licha : TDig = [1,3,5,7,9];

104 …pokračování... Ale pozor, nefunguje použití typované konstanty jako dimenze pole. Potřebujeme- li vytvořit dynamická pole, musíme postupovat jinak (viz dynamická a otevřená pole).

105 …pokračování Toto nefunguje! CONST Mez : integer = 28; TYPE Pole = ARRAY[1..Mez] OF...

106 Deklarace procedur a funkcí Procedury + funkce = Metody procedury nevracejí hodnotu funkce vracejí hodnotu (libovolného typu) deklarace používá formální parametry, které jsou při volání nahrazeny skutečnými parametry (samozřejmě musejí být kompatibilního typu)

107 Hlavička a tělo metod Existují dvě varianty: –hlavička metody, za ní bezprostředně tělo –nebo hlavička (tzv. forward = dopředná deklarace) a na jiném místě hlavička i tělo; v takovém případě by obě hlavičky měly být totožné (používejte Ctrl+Shift+C) tělo procedury = blok všechny deklarace jsou v bloku lokální po opuštění bloku ztrácejí proměnné hodnotu

108 Příklady hlaviček PROCEDURE Jedna ( S:string; P,Q:real ); FUNCTION Dva : TDenTydne; FUNCTION Tri ( var A:byte; B:char ) : real; PROCEDURE Ctyri ( const X: string ); pokud se použijí specifikátory (override, overload, abstract, virtual, dynamic apod.), tak se píší jen za první hlavičku a oddělují se středníkem

109 Parametry metod není-li uvedeno jinak, předávají se hodnotou. Hodnota parametru se zkopíruje na zásobník, kde s ní metoda pracuje a po ukončení metody se ta hodnota zapomene uvnitř metody můžeme s hodnotou libovolně pracovat a měnit ji žádná změna parametru se nepřenese ven z metody

110 Parametry volané odkazem parametry označené jako VAR se předávají odkazem = jménem = adresou. Metodě se nepředává parametr jako takový, ale jen adresa jeho umístění v paměti. změny VAR parametrů jsou trvalé (je to způsob, jak exportovat výsledky ven) jako skutečné parametry lze za VAR parametry dosadit jen to, co má adresu (příklad: 3*(N+1) je špatně !!)

111 Příkazy všechny příkazy jazyka Pascal (Delphi) jsou strukturované

112 Příkazy: klasifikace... Jednoduchý příkaz –přiřazovací:=bez mezer! –volání procedury/funkce použitím jména a skutečných parametrů –skokGOTO –prázdný; ;

113 …pokračování Strukturované příkazy –složenýBEGIN … END –podmíněnýIF CASE –cykluFOR WHILE REPEAT –withWITH

114 Přiřazovací příkaz dvojznak :=bez mezery přiřazovat lze jen výrazy slučitelného (kompatibilního typu). Nemusejí to být jen totožné typy (může např. jít o 2 intervaly na tomtéž hostitelském typu apod.) přiřazuje se celá proměnná, i strukturovaná (tzn. netřeba přiřazovat po položkách). Výjimka u pointerů (stringy, objekty, …)

115 Konverze při přiřazení při přiřazení se automaticky provádí konverze INTEGER  REAL obráceným směrem to nejde !

116 Příkaz (volání) procedury, funkce zapíše se její jméno se skutečnými parametry (musejí být správného typu) srovnejte deklaraci: FUNCTION Xyz (A:integer; B,C: real) : string; a volání této funkce: S := Xyz ( N, 123, N*(22/7) ); Poznámka: další podrobnosti probereme později

117 Některé standardní procedury Rewrite (F)otevře soubor pro zápis Append (F)dtto, ale za koncem Reset (F)otevře soubor pro čtení Read (F, Var1, Var2, …)načtení Write (F, Var1, Var2, …)zápis CloseFile (F)zavření souboru

118 Některé standardní funkce... ABS (X)absolutní hodnota SQR (X)X 2 SQRT (X)odmocnina SIN (X), COS (X), ARCTAN (X) EXP (X)e X LN (X)ln e X TRUNC (X)odsekávání 3.7  3 ROUND (X)zaokrouhlení 3.7  4

119 …pokračování... ORD (X)ordinální číslo CHR (X)ASCII znak číslo X SUCC (X)následovník PRED (X)předchůdce ODD (X)True, je-li X liché EOF (F)True, je-li konec souboru F EOLN (F)True na konci řádky textového souboru

120 …pokračování... FORMAT (‘Toto je %d. formátovací %s‘, [ 1, ‘string‘] ); IntToStr (123); StrToInt (‘000321’); DateTimeToStr (Now); ShowMessage (‘Stiskni tlačítko‘); k := MessageDlg (‘Toto se vypíše‘, mtError, [mbYes, mbNo], );

121

122 Příkaz GOTO není strukturovaný, ale je užitečný aby se omezilo riziko jeho použití, jsou uměle kladeny překážky nutno definovat návěští (v části LABEL) návěštím nutno označit cíl skoku skok lze provádět jen uvnitř jednoho bloku!

123 Příklad GOTO LABEL CilSkoku; BEGIN ……. CilSkoku: ……. GOTO CilSkoku;

124 Prázdný příkaz neobsahuje nic: ; ; ; má hlavně syntaktický význam: způsobuje, že středník můžeme napsat skoro všude (je- li tam nadbytečný, pokládá se za prázdný příkaz)

125 Priorita operátorů NOT*+= /-<> DIVOR< <= MODXOR> >= AND

126 Složený příkaz „programové závorky“ BEGIN P1; P2; … Pn END středník zde být nemusí, ale může

127 Podmíněný příkaz IF IF podmínka THEN příkaz1 IF podmínka THEN příkaz1 ELSE příkaz2 podmínka příkaz1příkaz2 zde nesmí být středník !

128 Podmíněný příkaz CASE... příkaz1příkaz2příkazN

129 …pokračování CASE... CASE proměnná OF Varianta1:Příkaz1; Varianta2:Příkaz2; ….. VariantaN:PříkazN; END; za zapomenutí tohoto END se vyhazuje od zkoušky !

130 …pokračování CASE Obvyklá rozšíření: –interval namísto výčtu variant: 3..53, 4, 5 –ELSE pro neuvedené varianty nejsou zde žádné BREAKy apod. provede se jen správný příkaz (v Pascalu jsou příkazy paralelně, v C sériově!)

131 Cyklus FOR FOR i := Pondeli TO Nedele DO příkaz; FOR i := Nedele DOWNTO Pondeli DO příkaz pracuje s jakýmkoliv ordinálním typem krok může být jen +1 nebo -1 řídicí proměnná cyklu musí být z téhož bloku! příkaz smí být jen jeden (  BEGIN…END)

132 Cyklus WHILE - +

133 WHILE - pokračování WHILE podmínka DO příkaz; test je před příkazem, tedy příkaz nemusí proběhnout ani jednou ! cyklus ukončí hodnota False příkaz je jen jeden (složený BEGIN…END)

134 Cyklus REPEAT - +

135 REPEAT - pokračování oproti WHILE je „všecho naopak“: –test je až na konci, proto příkazy vždy proběhnou aspoň jednou –příkazů může být více –cyklus se ukončí hodnotou True typické pro nekonečný cyklus: REPEAT ….. UNTIL False

136 Nestandardní rozšíření cyklů BREAK - vyskočí se z cyklu ven a pokračuje se příkazem následujícím za cyklem CONTINUE - zvětší/zmenší se řídicí proměnná a zahájí se nový průchod cyklem EXIT (platí obecně, ne jen v cyklech) - vyskočí se ven z procedury/funkce

137 Příkaz WITH není to výkonný příkaz - nedělá nic viditelného usnadňuje programování při práci s recordy (jde o jakési „vytknutí před závorku“) doporučuje se hojně používat, protože kompilátor s ním vytváří optimalizovaný kód

138 Příklad WITH WITH Xyz DO BEGIN A := … D := B + Cena; END; namísto Xyz.A.. Xyz.D.. apod Cena D A B proměnná Xyz

139 Oblast platnosti identifikátorů identifikátor platí uvnitř bloku, ve kterém je definován sejdou-li se 2 stejné identifikátory, které označují každý něco jiného, tak má přednost ten lokálnější, tzn. vnitřnější. Méně lokální identifikátor jím je přemaskován, tedy není přístupný, ale nepřestal existovat!

140 Příklad kolize identifikátorů VAR A, B VAR A, Z zde platí A, B a neplatí A, Z zde platí A, B a A, Z, ale nelze použít A, protože je přemaskován A

141 ALGORITMY a datové struktury

142 Základní pravidlo OOP Algoritmy a data nikdy nelze posuzovat odděleně, ale vždy jen ve vzájemné jednotě a souvislostech.

143 Zásobník LIFO=Last In, First Out Realizace #1: pomocí pole –potřebujeme pole P a ukazatel Z na vrchol zásobníku (= index prvního volného místa) 123

144 … pokračování Zásobníku... –při zápisu se zapíše na (volné) místo, kam ukazuje Z, pak se Z posune na další volné místo. Přitom nutno kontrolovat přetečení, tzn. aby Z nepřesáhlo meze pole. –při čtení se nejdřív Z o jednu pozici vrátí a potom se čte z místa, kam ukazuje Z. Přitom nutno kontrolovat podtečení (tzn. aby Z nebylo menší než nejmenší index pole, čili aby se nečetlo, co ještě nebylo zapsáno)

145 …pokračování Zásobníku Realizace #2: pomocí spojového seznamu (viz později) - jedná se o jeho zvláštní případ zvlášť výhodné je použití zásobníku při rekurzi

146 Fronta FIFO=First In, First Out Realizace #1: pomocí pole –potřebujeme pole P, ukazatel Z na začátek fronty a ukazatel K na konec fronty 123

147 …pokračování Fronty... –při zápisu se zapíše na (volné) místo, kam ukazuje Z, pak se Z posune na další volné místo –při čtení se čte z místa, kam ukazuje K a po přečtení se K posune na další pozici. Přitom nutno kontrolovat podtečení (tzn. aby K nepředběhlo nebo nebylo rovno Z) KZ AHOJ

148 … pokračování Fronty Realizace #2: pomocí spojového seznamu (viz později) - jedná se o jeho zvláštní případ typické použití: klávesnice, tiskárna, … problém: pole by mělo být „nekonečné“ řešení: kruhová fronta

149 Kruhová fronta dosahuje se funkcí modulo: i:=(i+1) mod N je nutno také kontrolovat přetečení, tzn. aby začátek nedosáhl či nepřepsal konec

150 Volná paměť = heap Motivace: dosavadní prostředky neumožnily dynamicky měnit velikost nebo počet proměnných Volná paměť (Heap) se využívá jako prostor, ve kterém si můžeme dočasně vytvářet (a rušit) proměnné program statické proměnné (VAR) zásobník volná paměť

151 Dynamické proměnné Proměnné, které dynamicky (=za chodu a podle potřeby) vytváříme ve volné paměti se nazývají dynamické proměnné na začátku nepotřebujeme znát jejich počet podstatný rozdíl: ke statickým proměnným (VAR) přistupujeme přes jejich název (identifikátor) dynamické proměnné název nemají (a mít nemohou): jak se k nim přistupuje ?!

152 Pojem POINTERu Pointer = směrník = ukazatel vypovídá o proměnné 2 věci: –kde se proměnná v paměti nachází, tzn. určuje její adresu –jak se s proměnnou má pracovat, tzn. definuje její typ z hlediska syntaxe se chová jako každá jiná proměnná

153 Příklady deklarace pointeru... VAR JmenoPromenne : ^NejakyTyp; nebo JinaPromenna : JmenoTypuPointer; pokud jsme ovšem předtím provedli TYPE JmenoTypuPointer = ^NejakyTyp;

154 …a vysvětlení VAR P : ^real; tím jsme vytvořili statickou proměnnou P, dlouhou 4 byty. Ta (po přiřazení) bude obsahovat adresu do paměti (typicky do heapu) a na této adrese bude ležet proměnná typu real P paměť statických proměnných volná paměť (heap) obsah= adresa=

155 Konvence Podobně jako je zvykem, všechny typy označovat pomocí identifikátorů začínajících na T…, tak je zvykem pointery označovat pomocí identifikátorů začínajících na P…, např. type PReal = ^real; PPrvek = ^TPrvek;

156 Pomůcka stříška ^ před typem proměnné je zdegenerovaná šipka , která bývala na starých klávesnicích nad klávesou 6 to mělo mnemotechnický význam: P : ^real se dá číst jako „P je typu šipka (=ukazatel) na real“

157 New pro práci s dynamickou pamětí existuje procedura New. Jako parametr se jí předá pointer New z volné paměti „vyřízne“ tolik bytů, kolik je potřeba (podle toho, na jaký typ ten pointer ukazuje) a do pointeru přiřadí adresu té „vyříznuté“ paměti

158 Příklad New var P:^real; begin New (P); ve volné paměti se našlo místo pro 1 číslo real, na které ukazuje P P volná paměť (heap)

159 Poznámky kolik místa se má z volné paměti „vyříznout“, pozná New podle toho, na jaký typ P ukazuje obsah paměti, na kterou P po provedení New(P) ukazuje, není definován. Proto je třeba přiřadit si do P^ nějakou hodnotu, než budeme P^ používat

160 Dispose Obrácenou operací k New je Dispose Za to, aby se nepotřebné dynamické proměnné vrátily systému pomocí Dispose, je odpovědný programátor ! Proceduře Dispose se jako parametr předá pointer na dynamickou proměnnou. Dispose „přilepí“ místo zabrané tou proměnnou zpátky k volné paměti POZOR, Dispose hodnotu P nezmění, ale P^ se pak už nesmí používat !

161 Fragmentace paměti Nevýhodou častého používání New a Dispose je, že časem dojde ke fragmentaci paměti. Některé jazyky používají Garbagge Collector (obdoba Defrag pro paměť), ale Delphi nikoliv (problém real-time aplikací) Pascal používá jiný mechanismus v Delphi se objektový mechanismus stará o správné Dispose nepotřebných objektů

162 Dereference... Základní operace s pointerem se nazývá dereference. Je to nalezení proměnné, na kterou pointer ukazuje. Označuje se šipkou (resp. stříškou): Je-li P : ^real; tak P^ je dereference která znamená „vyjdi z P, jdi po směru šipky a pracuj s proměnnou, na kterou tam narazíš“

163 …vysvětlení P^ tedy znamená proměnnou (v našem případě typu real) velký praktický význam mají pointery na typ record P volná paměť (heap) proměnná real adresa=

164 funkce Addr (X) Rozšíření jazyka: funkce P := Addr (X); vrací adresu (pointer) na libovolnou proměnnou X. Platí i na statické proměnné. Tato funkce je kompatibilní s libovolným pointerem. Platí rovnost Addr(X)^ = X v Delphi má stejný význam i (nedoporučuji používat)

165 Pointer a položky recordu Pokud P je pointer na nějaký record, pak P^ znamená celý tento record. Na jednotlivé položky recordu se můžeme odvolávat běžným způsobem pomocí „tečkové notace“. Například: –P^.Jmeno …v recordu, na který ukazuje pointer P, bereme položku Jmeno (string) –P^.Dalsi …v recordu, na který ukazuje pointer P, bereme položku Dalsi (pointer)

166 Přiřazování pointerů Z hlediska syntaxe je pointer proměnná jako každá jiná; lze ji tedy i přiřazovat (při zachování pravidel kompatibility; přiřazovat lze mezi ukazateli na kompatibilní typy) NIL je kompatibilní s jakýmkoliv pointerem a znamená „pointer který neukazuje nikam“ (má hodnotu $0000) typ POINTER je kompatibilní s jakýmkoliv ukazatelem (ale nelze ho dereferovat!)

167 Pozor při přiřazování Při přiřazování pointerů je třeba dávat pozor: existují 2 různé zápisy; oba jsou správné, ale každý znamená něco jiného! varianta 1 P := Q; varianta 2 P^ := Q^;

168 Přiřazení 1 P := Q; způsobí, že P bude ukazovat na stejnou adresu jako Q tedy P^ i Q^ dá stejnou hodnotu 5678 ale P i Q teď ukazují na tutéž proměnnou k proměnné „1234“ se ztratil přístup (!) P Q P Q

169 Přiřazení 2 P^ := Q^; způsobí, že do P^ se zkopíruje obsah Q^ tedy P^ i Q^ dá stejnou hodnotu 5678 P i Q pořád jsou různé proměnné P Q P Q

170 Použití pointerů v recordech Typické je používání pointerů v recordech: TPrvek = record Jmeno:string; Plat:real; … Další:^TPrvek; end; Výjimka z pravidla, že identifikátor musí být dřív nadeklarován, než je použit (Zdůvodnění: každý pointer má délku 4 byty, lze mu tudíž přiřadit místo v paměti)

171 Vysvětlení... Položka Další v recordu TPrvek na první pohled vypadá, jako by šlo o nějakou divnou rekurzi, při které record ukazuje sám na sebe  Tak to ale vůbec není! Další neukazuje na stejný record, nýbrž na jiný record stejného typu

172 … vysvětlení prostřednictvím položky „Další“ se recordy mohou navzájem různě propojovat typické použití: seznamy Jméno Plat Další Jméno Plat Další

173 Jednoduchý spojový seznam Jednoduchý = propojený 1 pointerem Spojový = realizovaný pomocí pointerů (spojů) Seznam = nějaká dataDalšínějaká dataDalší nějaká data Další 

174 Seznam Aby se k seznamu dalo přistupovat, potřebujeme jednu statickou proměnnou, která bude ukazovat na jeho začátek (obvykle se nazývá Hlava) Ostatní prvky seznamu už mohou být vytvořeny ve volné paměti (heapu)

175 Vytvoření seznamu Na začátku je seznam prázdný, tzn. neobsahuje žádný prvek, proto Hlava neukazuje nikam, má hodnotu NIL var Hlava:^TPrvek; begin Hlava := Nil;

176 Přidat prvek na začátek seznamu na začátku máme hlavu seznamu Hlava (obsahuje NIL) a pomocnou proměnnou Pom (typu ^TPrvek) Hlava X Pom X

177 Přidat prvek na začátek (krok 1) pomocí New vytvoříme nový prvek, na který ukazuje Pom do nového prvku naplníme data Jméno Plat Hlava X Pom

178 Přidat prvek na začátek (krok 2) to, co předtím bylo v pointeru Hlava, překopírujeme do položky Další toho nového prvku (v našem příkladu tam byla hodnota NIL, proto se do Pom^.Další vlozi NIL) Pom^.Další := Hlava; Hlava X Pom Jméno Plat X

179 Přidat prvek na začátek (krok 3) do pointeru Hlava nakopírujeme obsah Pom, čili adresu nového prvku tím je přidání skončeno poznámka: nemohli jsme provést rovnou New(Hlava), protože by se nám tím ztratil původní obsah pointeru Hlava HlavaPom Jméno Plat X

180 Přidat prvek na začátek (příklad) varPom: ^TPrvek; begin New(Pom); Pom^.Jmeno := …..; Pom^.Další := Hlava; Hlava:= Pom; HlavaPom Jméno Plat X

181 Přidat prvek na začátek (další) postupovali jsme takto, protože to je univerzální algoritmus, který lze aplikovat i na částečně už zaplněný seznam HlavaPom Jméno Plat X Jméno Plat

182 Procházení seznamem seznamem se dá procházet jen jedním směrem potřebujeme pomocný pointer Pom začíná se vždy v Hlava Pom := Hlava; procházení: Pom := Pom^.Další; Jméno Plat X Jméno Plat Jméno Plat Hlava

183 Seznam x zásobník Povšimněte si, že když procházíme seznam od Hlavy ke konci, pořadí prvků je od nejnovějšího po nejstarší, tedy LIFO tedy zásobník lze realizovat jako specielní případ seznamu, u kterého přidáváme na začátek a čteme od Hlava

184 Přidat prvek na konec seznamu na konec seznamu se přidává špatně, protože bychom museli nejprve ten konec najít (což je zdlouhavé) proto vedle pointeru Hlava zavedeme ještě další pointer ZaKonec, který bude ukazovat za konec seznamu na sentinel za koncem seznamu vytvoříme pomocný prvek, sentinel (nárazník)

185 Vytvoření seznamu ani nově vytvořený seznam tedy nebude prázdný, protože bude obsahovat sentinel begin New(Hlava); ZaKonec := Hlava; Hlava^.Další := Nil; (nic) X HlavaZaKonec

186 Práce se seznamem Prohledávání tohoto seznamu i přidávání prvků na začátek je stejné

187 Vložení prvku na konec (krok 1) do sentinelu se naplní data Jméno Plat X HlavaZaKonec...

188 Vložení prvku na konec (krok 2) za původním sentinelem se vytvoří sentinel nový New(ZaKonec^.Dalsi); Jméno Plat HlavaZaKonec... (nic)

189 Vložení prvku na konec (krok 3) pointer ZaKonec se přesune na nový sentinel doplní se NIL v novém sentinelu ZaKonec := ZaKonec^.Dalsi; ZaKonec^.Další := Nil; Jméno Plat HlavaZaKonec... (nic) X

190 Seznam x fronta Povšimněte si, že když procházíme od Hlavy seznam se vkládáním „na konec“, pořadí prvků je od nejstaršího po nejnovější, tedy FIFO tedy frontu lze realizovat jako specielní případ seznamu, u kterého přidáváme na konec a čteme od Hlava

191 Vložení prvku doprostřed Pro vkládání „doprostřed“ i pro další operace potřebujeme nějak označit místo v seznamu, se kterým pracujeme proto zavedeme další statickou proměnnou Značka Značka bude vždy ukazovat na ten prvek seznamu, se kterým pracujeme

192 Vložení prvku za značku (krok 1) využijeme pomocnou proměnnou k vytvoření a naplnění nového prvku New(Pom); Pom^.Jmeno := …; Jméno Plat ZnačkaPom Jméno Plat

193 Vložení prvku za značku (krok 2) adresu následujícího prvku nakopírujeme do nově vytvořeného prvku Pom^.Další := Značka^.Další; Jméno Plat ZnačkaPom Jméno Plat

194 Vložení prvku za značku (krok 3) adresu nového prvku vložíme do „Další“ prvku, na který ukazuje značka Značka^.Další := Pom; Jméno Plat ZnačkaPom Jméno Plat

195 Odstranit prvek za značkou Podobně jako vložit, i odstranit prvek za značkou je snadné

196 Odstranit prvek za značkou(1) do pomocné proměnné Pom si zapamatujeme adresu rušeného prvku Pom := Značka^.Další Jméno Plat Značka toto odstranit Jméno Plat Pom

197 Odstranit prvek za značkou(2) změníme pointer na následující prvek seznamu Značka^.Další := Pom^.Další Jméno Plat Značka toto odstranit Jméno Plat Pom

198 Odstranit prvek za značkou(3) nepotřebný prvek zrušíme Dispose (Pom); Jméno Plat Značka Jméno Plat Pom

199 Odstranit prvek se značkou Přestože se zdánlivě jedná o podobný případ, je to mnohem obtížnější Důvod: nevíme, odkud vychází modrý pointer, proto ho nemůžeme změnit, jak bychom potřebovali (červená) Značka toto odstranit Jméno Plat

200 Možnosti řešení najít ten neznámý „předcházející“ prvek a tím to převést na řešení předchozí úlohy „odstranit prvek za značkou“ - pomalé ! nebo znát „fintu“ (viz dále) Poznámka: u zkoušky nutno buď znát fintu, nebo umět napsat program, jímž se najde „předcházející“ prvek

201 Finta (výchozí stav) toto je výchozí stav Značka toto odstranit Jméno Plat

202 Finta (krok 1) na prvek, následující za prvkem označeným značkou, si nastavit pomocnou proměnnou Pom := Značka^.Další; Značka Jméno Plat Pom toto odstranit

203 Finta (krok 2) do prvku, na kterém je značka, nakopírovat celý prvek za ním následující úloha se tím vlastně převedla na předcházející úlohu „odstranit prvek za značkou“, kterou už umíme řešit Značka^ := Pom^; Značka Jméno Plat Jméno Plat Pom

204 Finta (krok 3) pomocí Dispose zrušit prvek, na který ukazuje Pom Dispose(Pom); Značka Jméno Plat Pom X

205 Vložit prvek před značku Úloha: vložte nový prvek před prvek, na který ukazuje značka Problém: stejný jako při „odstranit prvek se značkou“ Řešení: podobné jako v předchozím. Vložit nový prvek za značku a pak do něj obsah prvku se značkou okopírovat. Nakonec vložit nová data tam, kam ukazuje Značka

206 Seznam je trojice Trojice pointerů Hlava + ZaKonec + Značka je pro seznam tak častá a charakteristická, že někteří autoři pod pojmem „seznam“ chápou právě tuto trojici

207 Seznam - realizace pomocí pole Byla ukázána realizace seznamu pomocí dynamických proměnných; to ale neznamená, že seznam nelze realizovat statickými proměnnými, např. pomocí pole: ZnačkaZaKonec AHOJ volné místo

208 Uspořádaný seznam pro Seznam je typické, že prvky se identifikují pomocí jejich pozice v seznamu pro zvýšení rychlosti hledání můžeme seznam uspořádat, tzn. jeho prvky seřadit podle hodnot jejich klíčů, ale není to moc výhodné (spojové seznamy nelze snadno půlit, viz metoda půlení) struktura, ve které jsou prvky identifikovány hodnotou svého klíče, se nazývá tabulka

209 Dvojitý seznam Největší nevýhodou jednoduchého seznamu je, že neumíme rychle najít předcházející prvek (procházení je jednosměrné) kde to vadí, použijeme dvojitě zřetězený seznam: (nějaká data) Nahoru Dolů (nějaká data) Nahoru Dolů (nějaká data) Nahoru Dolů

210 Stromy pomocí recordů, ze kterých vede více pointerů, se snadno realizují stromy nejčastější strom je dichotomický, který má z každého uzlu 2 pointery: na levý podstrom a na pravý podstrom

211 Strom - terminologie (nějaká data) Klíč LevýPravý (nějaká data) Klíč LevýPravý (nějaká data) Klíč LevýPravý kořen uzly větve listy podstrom (zde levý)

212 Binární strom Binární strom = dichotomický strom, pro který navíc platí, že všechny klíče v levém podstromu jsou menší a v pravém podstromu větší než klíč (předpoklad: klíče jsou jednoznačné)

213 Binární strom - příklady kdyby zde bylo 36 nebyl by binární

214 Binární strom - význam nejdůležitější vlastnost: snadné a velmi rychlé hledání půlením je-li N počet uzlů, tak počet kroků  log 2 N

215 Binární strom: hledáme klíč= <32, proto pokračuji levým podstromem 28>26, proto pokračuji praým podstromem Klíč nalezen. Dojde-li se na list, aniž by byl klíč nalezen, pak neexistuje.

216 Binární strom: vložit klíč= <32, proto pokračuji levým podstromem 27>26, proto pokračuji praým podstromem Klíč neexistuje. Protože 27<32, vkládáme ho do levé větve 27

217 Hromada = heap jedná se o jiný heap než byla volná paměť! Heap je forma, jak binární strom zapsat do pole (a ušetřit tak místo na pointery) vychází z toho, že binární strom má v kořenu 1 uzel, v 1. úrovni 2 uzly, ve 3. úrovni 4 uzly atd, tedy v N-té úrovni je 2 N uzlů

218 Heap - princip proto se v poli postupně pro i-tou úroveň přidělí 2 N pozic (místa pro uzly, které ve stromu chybí, zůstanou prázdná) poměrně jednoduchým algoritmem se dá najít vztah mezi pozicí uzlu v binárním stromě a mezi jeho indexem v poli=heapu protože pozice všech uzlů jsou známé, lze pomocí heapu realizovat mnohé algoritmy, které pomocí pointerů napsat nelze

219 Heap - znázornění

220 Heap - výpočet pozice nechť kořen je na indexu 1 jestliže jsme v uzlu s indexem K, tak (pokud existují): jeho rodič má index K div 2 levý podstrom má index 2*K pravý podstrom má index 2*K + 1

221 Nevýhoda binárního stromu při nevhodném pořadí vkládaných uzlů mohou větve narůstat nerovnoměrně, až v krajním případě může strom zdegenerovat na lineární seznam: 32 X X X X

222 AVL stromy odstraňují hlavní slabinu binárního stromu AVL strom je binární strom, u kterého navíc pro každý jeho uzel platí, že výška levého podstromu se od výšky pravého podstromu liší nejvýše o 1 abs( High(L) - High(P) )  1 čili Nevyváženost  1

223 Výška (pod)stromu levý = 3pravý = 2

224 Nevyváženost (pod)stromu nevyváženost = 1

225 AVL strom - jak dosáhnout pomocí Nevyváženosti je definováno, co AVL strom je, ale nijak se tím neřeší, jak ho vytvořit platí: každý binární strom lze přetransformovat na AVL strom, a to pomocí konečného počtu rotací, aplikovaných na vhodné uzly stromu

226 Pravá rotace (výchozí stav) Výchozí stav: toto je kořen, strom není AVL, protože Nevyváženost = 2 14

227 Pravá rotace (krok 1) Krok 1: z tohoto uzlu uděláme kořen 14 Poznámka: Kdyby se jednalo o rotaci podstromu, změníme pointer co vedl na uzel 32 tak, aby ukazoval na uzel 26. Problém: na kořen nesmí vést žádná větev

228 Pravá rotace (krok 2) Krok 2: směr této větve obrátíme, aby byla dodržena „podmínka pro kořen“ 14 Problém: z tohoto uzlu vedou 3 větve

229 Pravá rotace (krok 3) Krok 3: tuto větev přesuneme pod vedlejší uzel 14

230 Pravá rotace (výsledek)

231 Rotace - poznámky předchozí příklad převedl binární strom na AVL nikde ale není psáno, že každá rotace (a nad každým uzlem) musí vést k výsledku! Nevhodná aplikace může situaci i zhoršit. podobně jako pravá rotace, existuje i levá rotace. Je zcela symetrická (zkouší se!)

232 Rotace - princip aplikace princip, podle kterého se aplikují rotace, vychází z faktu, že k AVL stromu lze uzel přidat v zásadě 6 způsoby: levá rotace kolem uzlu A vyrovná nevyváženost na 0 A A B pravá rotace kolem uzlu A a následně levá rotace kolem uzlu B vyrovná nevyváženost na 1 přidané uzly

233 B-stromy pozor - „B“ neznamená binární, ale „Bayerovy“ doposud probrané struktury předpokládaly stejně rychlý přístup ke všem prvkům, což v praxi neplatí (paměť:disk až 10 6 ) a neomezenou velikost paměti (což také neplatí) B-stromy dávají dobrý návod, jak optimalizovat stránkování paměti (=přesouvání mezi pamětí a diskem) z hlediska rychlosti

234 B-stromy stránkování model stránkování (kdyby se do paměti vešly jen 5 uzlů):

235 B-stromy řádu K každá stránka obsahuje maximálně 2K a minimálně (s výjimkou kořene) K uzlů každá stránka je buď list (=nevedou z ní žádné větve) anebo z ní vede M+1 větví, kde M=aktuální počet uzlů ve stránce všechny listové stránky jsou na téže úrovni strom narůstá „vzhůru“, tzn. u kořene

236 B-strom řádu K=2, příklad stránky musejí mít 2..4 uzly: <10>20 mezi

237 B-strom, vložit 23 najít správnou stránku pokud je místo, vložit do stránky <25 >20

238 B-strom, vložit 17 najít správnou stránku protože už není místo, nutno přeuspořádat vybrat prostřední a vytlačit nahoru

239 B-strom, vloženo 17, výsledek kdyby se i horní stránka přeplnila, pokračovalo by se analogicky vzhůru

240 B-stromy, strategie stránkování lze předpokládat, že bude vyžadován přístup k datům „blízko sebe“ v paměti držet vrcholovou stránku + z každé úrovně stránku na kterou se přistupovalo naposledy

241 Hledání Předpoklady: jednoznačný (=neopakující se) klíč, žádné dodatečné informace, žádná dodatečná paměť

242 Přímé (indexové) hledání v tabulce tabulka musí mít podobu pole klíč musí být malé ordinální číslo klíč se přímo použije jako index do pole; na daném indexu se nacházejí data nevýhody: index musí být ordinální a vede k řídkému poli 0123 BACD 4567

243 Postupné prohledávání nejtriviálnější, ale pomalé  N i := 0; while (i HledanyKlic) do i := i+1; if Pole[i].Klíč=HledanyKlic then Nalezeno; 0123 CBAD 4567

244 Postupné prohledávání, nárazník cíl: odstranit zbytečný test na konec pole za konec pole vložit hledanou hodnotu klíče (nárazník=sentinel) tím je zaručeno, že se klíč vždycky najde po ukončení cyklu nutno testovat, zda bylo nalezeno uvnitř pole, nebo zda se jedná o sentinel

245 Nárazník = sentinel i := 0; Pole[Počet].Klíč := ‘K‘; while Pole[i].Klíč <> ‘K‘` do i := i+1; if i

246 Metoda půlení pole dodatečná podmínka: setříděnost tabulka musí mít podobu pole velká výhoda: rychlost  log 2 N je možno použít na jakýkoliv klíč (i nečíselný, např. na stringy) velká nevýhoda: data se musejí udržovat uspořádaná, což zvyšuje režii vhodné: kde se málo vkládá a často čte

247 Metoda půlení (krok 1) máme 2 indexy: Dn=0 a Up=PočetKlíčů nalezneme Střed := (Dn+Up) div 2; 0123 ABCDZQXY 4567 DnUpStřed

248 Metoda půlení (krok 2) předpokládejme, že hledáme klíč „C“ porovnáme klíč s obsahem prvku pod indexem Střed (je to „D“) protože klíč

249 Metoda půlení (krok 3) znovu najdeme Střed:=(Up+Dn) div 2=„B“ protože klíč

250 Metoda transformace klíče = metoda hashování [hešování] vychází z přímého (indexového) hledání odstraňuje jeho nedostatky (řídké pole a nutnost ordinálního indexu) libovolný typ indexu (např. string) velmi rychlý přístup

251 Transformace klíče, příklad klíčem nechť je plat metodu vysvětlíme na příkladu podle vedlejší tabulky zvolím hashovací funkci f(X):=X mod 10 pozor, to není dobrá volba (zde jsem ji použil jen pro její názornost) Mozart2530 Brahms1893 Beethoven5422 Chopin2215 Bach6048 Čajkovskij4914 Verdi3175

252 Transformace klíče, vkládání údaje budeme vkládat do pole A délky 10 vkládáme tak, že klíč nejprve přetransformujeme hashovací funkcí a pak číslo, které dostaneme, použijeme jako index do pole A stejně se postupuje pro všechny údaje … … Chopin: 2215 h(2215) = 5 vložit sem

253 Transformace klíče, kolize v okamžiku, kdy se pokusíme vložit Verdi: 3175 nastane problém: pozice už je obsazena ! nastala kolize existují 2 způsoby řešení kolize: –vnitřní –vnější 0Mozart 1 2Beethoven 3Brahms 4Čajkovskij 5Chopin 6 7 8Bach 9 Verdi: 3175 h(3175) = 5 KOLIZE

254 Vnější řešení kolize vnější, protože se řeší vně hashovacího pole princip: pozice nalezená hashováním je současně hlavou spojového seznamu (kolizní seznam) 5Chopin: 2215 Verdi:

255 Vnitřní řešení kolize (1) údaje se ukládají uvnitř hashovacího pole je třeba upravit algoritmus ukládání i hledání ! když nastane kolize, hledá se první volné místo a na to se údaj uloží

256 Vnitřní řešení kolize (2) modifikace: vkládání na nejbližší volné místo (index+1) vede k shlukům proto se někdy krok volí proměnlivý podle nějaké tabulky hledání tomu musí odpovídat 0Mozart: Beethoven: Brahms: Čajkovskij:4914 5Chopin: Verdi: Bach: Verdi: 3175 h(3175) = 5 Kolize index 5 je obsazen, proto vložíme na nejbližší volný (6)

257 Vnitřní řešení kolize (3) jiný příklad: někdy se musí dát dost daleko Hledání: hledá se buď klíč, nebo první volné místo ! 0Mozart: Beethoven: Brahms: Čajkovskij:4914 5Chopin: Verdi: Dvořák: Bach: Dvořák: 2723 h(2723) = 3 Kolize index 3 je obsazen, proto vložíme na nejbližší volný ( až 7! )

258 Vnitřní řešení kolize (4) Problém: při vnitřním řešení kolize není možno údaje vymazat! kdybychom např. vymazali Chopin:2215, vzniklo by volné místo, které by znemožnilo nalezení Dvořák i Verdi proto se „vymazané“ údaje jen označí jako neplatné a při nejbližší příležitosti přepíší 0Mozart: Beethoven: Brahms: Čajkovskij:4914 5Chopin: Verdi: Dvořák: Bach:

259 Volba hashovací funkce cílem je zvolit funkci, která –údaje rozprostře rovnoměrně –z typu klíče udělá ordinální číslo omezené velikosti (odpovídající velikosti hashovacího pole) z tohoto hlediska DIV ani MOD 10 nejsou dobré dobré jsou např. MOD Prvočíslo, MD5, … stringy a recordy možno brát jako binární

260 Třídění = řazení Předpoklady: jednoznačný (=neopakující se) klíč, žádné dodatečné informace, žádná dodatečná paměť

261 Bublinkové třídění Bubblesort nejtriviálnější, ale nejméně účinné  N 2 postupné přesouvání dat připomíná „probublávání“ bublin v kapalině Postupně se testují dvojice za sebou jdoucích dat: –Pokud první údaj je větší než druhý, navzájem se přehodí. –Přejde se na další dvojici

262 Bubblesort (příklad) Klíče jsou ve správném pořadí (15<68), žádná akce 2. Nesprávné pořadí, prohodit 68 a Klíče jsou ve správném pořadí (teď se testuje 68 proti 84), žádná akce …. a tak dále až do konce dat

263 … pokračování nový průchod atd.

264 Bubblesort - počet průchodů na efektivnost bubblesortu má vliv, kolikrát se polem dat prochází nejjednodušší podmínka: je-li dat N, pole se projde N-krát. Protože při každém průchodu se hodnota posune aspoň o 1 pozici, jsou po N průchodech i v nejnepříznivějším případě všechna data na svém místě

265 Bubblesort II podmínku N průchodů lze modifikovat: –zavede se pomocná proměnná, která se na začátku každého průchodu nastaví na False –kdykoliv dojde k prohození dat, pomocná proměnná se nastaví na True –pokud je po skončení průchodu pomocná proměnná False, nedošlo k žádnému prohození, tudíž data jsou setříděna a je možno skončit

266 Bubblesort III efektivnost lze ještě dále zvýšit tím, že se nebudou procházet všechna data: –při každém průchodu si poznamenáme pozici P1 první změny a P2 poslední změny –následující průchod se provádí jen od P1-1 do P2+1

267 Shakesort nepříjemnou vlastností bubblesortu je nesymetrie: „84“ se na správné místo dostala už během prvního průchodu, zatímco „18“ postoupila jen o 1 pozici řešení: pole střídavě procházet shora a zdola programová realizace: dva cykly (to/downto) za sebou

268 Shellsort pokusy o urychlení: nejprve dlouhý krok, aby se data rychle dostala do blízkosti správné posice, pak postupně zjemňovat existují doporučené posloupnosti kroků

269 Insertsort další triviální, ale pomalý algoritmus  N2 funguje, jako když si člověk rovná v ruce karty: –nalevo už jsou setříděné, napravo ještě nesetříděné –z nesetříděných se postupně zleva doprava jedna vybere… –…a zasune na správné místo mezi setříděné (mezera se vytvoří posunutím karet)

270 Insertsort příklad vezmeme číslo a postupně ho od začátku porovnáváme se setříděnými, až nalezneme správnou pozici vytvoříme mezeru tak, že setříděné (od té mezery) posuneme o 1 pozici doleva číslo do vzniklé mezery vložíme a tím je zatříděno index=

271 Insertsort program for j:=Low(A)+1 to High(A) do begin Klic := A[j]; i := j-1; while (i>0)and(A[i]>Klic) do begin A[i+1] := A[i]; i := i-1; end; A[i+1] := Klic; end; index=

272 Quicksort vynikající rychlost řádu  N log 2 N, což je mezi tříděními bez dodatečných předpokladů nejlepší známý výsledek kombinuje rekurzi s metodou půlení

273 Quicksort krok 1 zvolíme medián. (Medián je prvek s prostřední hodnotou.) Protože nalezení mediánu je časově náročné, nahrazuje se nalezením prvku ležícího uprostřed (zhoršení rychlosti je obvykle malé) obvykle se realizuje pomocí div

274 Quicksort krok 2 postupujeme zleva doprava a najdeme první prvek větší než medián postupujeme zprava doleva a nalezneme první prvek menší než medián oba prvky spolu prohodíme

275 Quicksort krok 2 dokončení kdyby se (jako nám) stalo, že na jedné straně už nebude s čím prohazovat, prohodíme s mediánem skončíme, když všechny prvky menší než medián jsou nalevo a všechny prvky větší napravo od mediánu

276 Quicksort krok 3 skončili jsme ve stavu, kdy nalevo od mediánu jsou všechny prvky menší než medián (byť nesetříděné) a napravo všechny prvky větší než medián na obě tyto části rekurzívně aplikujeme stejný algoritmus rekurze

277 Třídění v lineárním čase z toho, že Quicksort je nejrychlejší algoritmus pracující bez dodatečných předpokladů nelze usuzovat, že je za všech okolností nejlepší ukážeme 2 algoritmy, které s poměrně malými předpoklady dosahují dokonce lineární rychlosti, tj. rychlost  N

278 Počítací třídění Counting sort předpoklad: pro nějaké (ne moc velké) K všechny klíče jsou typu integer  0..K základní myšlenka: když pro každý prvek X víme, kolik prvků je menších, můžeme X rovnou zatřídit příklad: jestliže víme, že 17 prvků je menších než X, tak X musí ležet na 18. pozici s malou modifikací lze zpracovat i případ více shodných klíčů

279 Counting sort princip (1) Budeme pracovat se 3 poli: A[1..N] bude obsahovat vstupní data, B[1..N] setříděná data a C[1..K] je pomocné. N je počet dat a K je limit velikosti klíče s pomocí této znalosti přeřadíme prvky z B do A ? ? ? ? A B C

280 Counting sort princip (2) do C[i] napočítáme počet prvků s klíčem rovným i hodnoty sečteme tak, aby C[i] obsahovalo počet prvků s klíčem menším než i A C A C

281 Counting sort princip (3) postupně pro všechny prvky z A, zprava doleva: vezmeme prvek jeho klíč použijeme jako index do C hodnotu nalezenou v C použijeme jako index do B, kam uložíme prvek A C ? ? ? ? ? ? 3 ? B

282 Counting sort princip (4) předcházející postup by vyhovoval, kdyby všechny klíče byly jedinečné připouštíme-li opakování klíčů, po každém zápisu do B musíme zmenšit C[i] aby následující zápis šel na předchozí index takže v našem příkladu se C[i] zmenší ze 7 na 6 a následující klíč 3 už půjde do B[6] výsledné pole B po zpracování: B

283 Counting sort program for i := 0 to K do C[i] := 0; for i := 1 to High(A) do C[ A[i] ] := C[ A[i] ] + 1; for i := 1 to K do C[i] := C[i] + C[i+1]; for i := High(A) downto 1 do begin B[ C[A[i]] ] := A[i]; C[ A[i] ] := C[ A[i] ] - 1;// dec(….) end;

284 Bucket sort bucket = kbelík dává dobrou rychlost (skoro lineární  N) za předpokladu rovnoměrného rozložení, tzn. že všechny klíče jsou stejně pravděpodobné funguje na klíče real  <0..1) nebo když lze klíče na tento interval přetransformovat

285 Bucket sort princip interval <0..1) se rozdělí na N stejných podintervalů ( to jsou ty kýble ) protože rozdělení je rovnoměrné, do každého intervalu padne jen málo klíčů, takže setřídění uvnitř intervalu je snadné a rychlé (trochu to připomíná hashování) postupně projdeme intervaly a v nich klíče podle velikosti

286 Bucket sort příklad for i := 1 to High(A) do begin Podle A[i] určit interval j; Přidat A[i] k seznamu začínajícímu v B[j]; end; for j := 0 to 4 do Setřídit seznam B[j]; Spojit seznamy 0,78 0,17 0,39 0,26 0, Vstup (pole A): | | | | Pracovní pole B: 0 0,2 0,4 0,6 0,8 1 0,780,170,39 0,260,72

287


Stáhnout ppt "AII = Algoritmy pro inženýrskou informatiku Přednášky."

Podobné prezentace


Reklamy Google