Stáhnout prezentaci
Prezentace se nahrává, počkejte prosím
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 mnoho dalších registrů Periférie PC=čítač instrukcí RAM,ROM PSW, SP, ... 100: 37 101: 02 102: 114 103: 56 R/W střadač (akumulátor) dekodér instrukcí ALU μP DB
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 232 starší mikroprocesory: šířka jen 20 (24) bitů, adresovací prostor 220 = 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á?! 200 hlavní program 900 funkce 980 300
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: 201 2001: 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: 201 2001: 956 2002: ... 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: 201 2001: 956 2002: ... 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: 201 2001: 956 2002: ... 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 výpočet adresy načtení operandu 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 knihovny mezikód 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 monitorovací uzel stanice C
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 DATA zabezpečení
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 A stanice D stanice B stanice C
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 A stanice D stanice B stanice C
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 A stanice B stanice 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í... teorie skutečně přenesené pakety bod zahlcení
požadavky na přenos
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
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; tečka BLOK
70
Procedury a funkce PROCEDURE NazevProcedury (Parametry);
FUNCTION NazevFunkce (Parametry) : Typ; BLOK BLOK
71
Konstanty Pi = 3.141; DlouheCislo = 245897556174;
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 reálné typy
INTEGER 4 byty, a četné odvozené CHAR ASCII, nebo WideChar…UNICODE BOOLEAN jen TRUE nebo FALSE výčtový typ (Karel, Jan, Martin, Lojza) interval reálné typy REAL 8 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 Záznam (record) Množina Soubor
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 1..1024;
ARRAY [1..5] OF ARRAY [-6..30] OF ARRAY [1..2] OF Byte; ARRAY [1..5, , 1..2] OF Byte;
79
String = zvláštní případ pole
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 1=RefC 4=délka A H O J ? ?
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
(doplněno podle nejdelší varianty)
…vysvětlení se selektorem záznamu: bez selektoru záznamu Cena Cena Cena Cena D D A X A X B B 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
TYPE TMnozina = SET OF CHAR; VAR X, 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
VAR F : 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
VAR F : 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
VAR F : 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
VAR F : 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
VAR F : 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…. počet bytů na 1 položku poslední parametr je nepovinný
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 = -0.723;
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
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: tělo procedury = blok
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ů skok GOTO prázdný ; ;
113
…pokračování Strukturované příkazy složený BEGIN … END podmíněný IF
CASE cyklu FOR WHILE REPEAT with WITH
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) X2 SQRT (X) odmocnina SIN (X), COS (X), ARCTAN (X) EXP (X) eX LN (X) lneX 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], );
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 * + = / - <> DIV OR < <=
/ - <> DIV OR < <= MOD XOR > >= AND
126
středník zde být nemusí,
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 zde nesmí být středník ! podmínka příkaz1 příkaz2
128
Podmíněný příkaz CASE... příkaz1 příkaz2 příkazN
129
za zapomenutí tohoto END
…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í: 3..5 3, 4, 5
interval namísto výčtu variant: , 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;
proměnná Xyz WITH Xyz DO BEGIN A := … D := B + Cena; END; namísto Xyz.A.. Xyz.D.. apod Cena D A B
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ů
zde platí A, B a A, Z, ale nelze použít A , protože je přemaskován A VAR A, B VAR A, Z zde platí A, B a neplatí A, Z
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) 3 2 1
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 3 2 1
147
…pokračování Fronty... A H O J K Z 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)
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
statické proměnné (VAR)
Volná paměť = heap program statické proměnné (VAR) zásobník volná paměť 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é
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 paměť statických proměnných P 4567 obsah= 3.141 adresa= 4567 volná paměť (heap)
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 4567 proměnná real adresa= 4567 volná paměť (heap)
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 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 (!) 1234 Q 5678 P 1234 Q 5678
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é 1234 Q 5678 P 5678 Q 5678
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á data Další nějaká data Další 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
Hlava Pom na začátku máme hlavu seznamu Hlava (obsahuje NIL) a pomocnou proměnnou Pom (typu ^TPrvek) X X
177
Přidat prvek na začátek (krok 1)
Hlava Pom pomocí New vytvoříme nový prvek, na který ukazuje Pom do nového prvku naplníme data X Jméno Plat
178
Přidat prvek na začátek (krok 2)
Hlava Pom 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; X Jméno Plat X
179
Přidat prvek na začátek (krok 3)
Hlava Pom 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 Jméno Plat X
180
Přidat prvek na začátek (příklad)
Hlava Pom var Pom: ^TPrvek; begin New(Pom); Pom^.Jmeno := …..; Pom^.Další := Hlava; Hlava := Pom; 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 Hlava Pom Jméno Jméno Plat Plat X
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ší; Hlava Jméno Plat Jméno Jméno Plat Plat X
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; Hlava ZaKonec (nic) (nic) X
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 Hlava ZaKonec ... Jméno Plat X
188
Vložení prvku na konec (krok 2)
za původním sentinelem se vytvoří sentinel nový New(ZaKonec^.Dalsi); Hlava ZaKonec ... Jméno Plat (nic) (nic) (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; Hlava ZaKonec ... Jméno Plat (nic) (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)
Pom Značka využijeme pomocnou proměnnou k vytvoření a naplnění nového prvku New(Pom); Pom^.Jmeno := …; Jméno Jméno Plat Plat
193
Vložení prvku za značku (krok 2)
Pom Značka adresu následujícího prvku nakopírujeme do nově vytvořeného prvku Pom^.Další := Značka^.Další; Jméno Jméno Plat Plat
194
Vložení prvku za značku (krok 3)
Pom Značka adresu nového prvku vložíme do „Další“ prvku, na který ukazuje značka Značka^.Další := Pom; Jméno Jméno Plat 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ší Značka Pom Jméno toto Jméno Plat odstranit Plat
197
Odstranit prvek za značkou(2)
změníme pointer na následující prvek seznamu Značka^.Další := Pom^.Další Značka Pom Jméno toto Jméno Plat odstranit Plat
198
Odstranit prvek za značkou(3)
nepotřebný prvek zrušíme Dispose (Pom); Značka Pom Jméno Jméno Plat Plat
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 Jméno odstranit 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 Jméno odstranit
Plat
202
Finta (krok 1) Značka Pom na prvek, následující za prvkem označeným značkou, si nastavit pomocnou proměnnou Pom := Značka^.Další; toto Jméno odstranit Plat
203
Finta (krok 2) Značka Pom 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^; Jméno Jméno Plat Plat
204
Finta (krok 3) pomocí Dispose zrušit prvek, na který ukazuje Pom
Značka Pom X pomocí Dispose zrušit prvek, na který ukazuje Pom Dispose(Pom); Jméno Plat
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: A H O J volné místo Značka ZaKonec
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) (nějaká data) (nějaká data) Nahoru Nahoru Nahoru Dolů Dolů 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) kořen podstrom (zde levý) Klíč uzly
Pravý uzly větve (nějaká data) (nějaká data) Klíč Klíč listy Levý Pravý Levý Pravý
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
32 45 26 kdyby zde bylo 36 nebyl by binární 14 63 28
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ů log2N
215
Binární strom: hledáme klíč=28
28<32, proto pokračuji levým podstromem 28>26, proto pokračuji praým podstromem 32 45 26 14 63 28 Klíč nalezen. Dojde-li se na list, aniž by byl klíč nalezen, pak neexistuje.
216
Binární strom: vložit klíč=27
27<32, proto pokračuji levým podstromem 27>26, proto pokračuji praým podstromem 32 45 26 14 63 28 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 2N uzlů
218
Heap - princip proto se v poli postupně pro i-tou úroveň přidělí 2N 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í 1 2 3 4 5 6 7 8 9 10 11 12 13 ...
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 33 X 34 X 35 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 32 levý = 3 pravý = 2 45 26 14 63 28 27
224
Nevyváženost (pod)stromu
32 45 26 14 63 28 nevyváženost = 1 27
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)
toto je kořen, strom není AVL, protože Nevyváženost = 2 32 45 26 14 28 27
227
Krok 1: z tohoto uzlu uděláme kořen
Pravá rotace (krok 1) 32 Krok 1: z tohoto uzlu uděláme kořen Problém: na kořen nesmí vést žádná větev Poznámka: Kdyby se jednalo o rotaci podstromu, změníme pointer co vedl na uzel 32 tak, aby ukazoval na uzel 26. 45 26 14 28 27
228
směr této větve obrátíme,
Pravá rotace (krok 2) 32 Krok 2: směr této větve obrátíme, aby byla dodržena „podmínka pro kořen“ Problém: z tohoto uzlu vedou 3 větve 45 26 14 28 27
229
Pravá rotace (krok 3) 32 Krok 3: tuto větev přesuneme
pod vedlejší uzel 45 26 14 28 27
230
Pravá rotace (výsledek)
26 14 32 28 45 27
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: A B A levá rotace kolem uzlu A vyrovná nevyváženost na 0 přidané uzly pravá rotace kolem uzlu A a následně levá rotace kolem uzlu B vyrovná nevyváženost na 1
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ž 106) 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: 25 10 20 30 40 <10 >20 mezi 22 24
237
B-strom, vložit 23 najít správnou stránku
pokud je místo, vložit do stránky <25 25 10 20 30 40 >20
238
B-strom, vložit 17 najít správnou stránku
protože už není místo, nutno přeuspořádat 10 20 17 13 14 17 18 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 25 30 40 13 14 17 18
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 1 2 3 4 5 6 7 B A C D
243
Postupné prohledávání
nejtriviálnější, ale pomalé N i := 0; while (i<Počet-1) and (Pole[i].Klíč <> HledanyKlic) do i := i+1; if Pole[i].Klíč=HledanyKlic then Nalezeno; 1 2 3 4 5 6 7 C B A D
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<Počet then Nalezeno; 1 2 3 4 5 6 7 C B A D K
246
Metoda půlení pole dodatečná podmínka: setříděnost
tabulka musí mít podobu pole velká výhoda: rychlost log2N 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; Dn Střed Up 1 2 3 4 5 6 7 A B C D Q X Y Z
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íč<Pole[Střed], vybereme dolní polovinu Up:=Střed-1 Dn Střed Up 1 2 3 4 5 6 7 A B C D Q X Y Z
249
Metoda půlení (krok 3) znovu najdeme Střed:=(Up+Dn) div 2=„B“
protože klíč<Pole[Střed], vybereme horní polovinu Dn:=Střed+1 postup se opakuje, dokud buď není nalezen klíč, nebo se nenajde konec Up<Dn Střed Dn Up 1 2 3 4 5 6 7 A B C D Q X Y Z
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) Mozart 2530 Brahms 1893 Beethoven 5422 Chopin 2215 Bach Čajkovskij 4914 Verdi
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 1 2 3 4 5 6 … 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ší 0 Mozart 1 2 Beethoven 3 Brahms 4 Čajkovskij 5 Chopin 6 7 8 Bach 9 Verdi: 3175 h(3175) = 5 KOLIZE
254
Vnější řešení kolize vnější, protože se řeší vně hashovacího pole 4
princip: pozice nalezená hashováním je současně hlavou spojového seznamu (kolizní seznam) 4 5 Chopin: 2215 6 Verdi: 3175
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 0 Mozart: 2530 1 2 Beethoven: 5422 3 Brahms: 1893 4 Čajkovskij:4914 5 Chopin: 2215 6 Verdi: 3175 7 8 Bach: 6048 9 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 ! 0 Mozart: 2530 1 2 Beethoven: 5422 3 Brahms: 1893 4 Čajkovskij:4914 5 Chopin: 2215 6 Verdi: 3175 7 Dvořák: 2723 8 Bach: 6048 9 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íší 0 Mozart: 2530 1 2 Beethoven: 5422 3 Brahms: 1893 4 Čajkovskij:4914 5 Chopin: 2215 6 Verdi: 3175 7 Dvořák: 2723 8 Bach: 6048 9
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é N2
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)... 15 68 17 84 21 18 33 96 1. Klíče jsou ve správném pořadí (15<68), žádná akce 2. Nesprávné pořadí, prohodit 68 a 17 3. 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í 15 68 17 84 21 18 33 96 15 17 68 84 21 18 33 96 15 17 68 21 84 18 33 96 15 17 68 21 18 84 33 96 15 17 68 21 18 33 84 96 15 17 68 21 18 33 84 96 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 15 17 68 84 21 18 33 96 15 17 68 21 84 18 33 96 15 17 68 21 18 84 33 96 15 17 68 21 18 33 84 96
268
Shellsort 15 17 68 84 21 18 33 96 15 17 68 84 21 18 33 96 15 17 21 18 68 84 33 96 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 index= 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
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 A[i+1] := A[i]; i := i-1; end; A[i+1] := Klic; index=
272
Quicksort vynikající rychlost řádu N log2N, 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 2
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 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)
A 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 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 3 C 7 B ? ? ? ? ? ? 3 ?
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 Vstup (pole A): 0,78 0,17 0,39 0,26 0,72 Pracovní pole B: ,2 0, ,6 0, | | | | 0,17 0,39 0,78 0,26 0,72
287
The End
Podobné prezentace
© 2024 SlidePlayer.cz Inc.
All rights reserved.