Pojďte s námi PROGRAMOVAT !

1. 12. 2008

Sdílet

Prosincová lekce programátorského kurzu jazyka C# 3.0 se věnuje vývoji prakticky orientovaných programů. Programován...


Prosincová lekce programátorského kurzu jazyka C# 3.0 se věnuje vývoji prakticky orientovaných programů. Programování smysluplných aplikací je bezesporu koncepcí, jež se u studentů těší značné oblibě. Během praktického programování se seznámíme s relačními operátory a rozhodovacími příkazy.


praktické cvičení:
Řešení kvadratické rovnice

Při úvodních lekcích programování v kterémkoliv programovacím jazyce dojde dříve nebo později na algoritmizaci výpočtu kořenů kvadratické rovnice. Kvadratická rovnice je rovnice ve tvaru ax2 + bx + c, přičemž platí, že a, b a c jsou libovolná reálná čísla, přičemž a je různé od nuly. V závislosti na požadované úrovni abstrakce finálního řešení volí studenti zpravidla dvě cesty řešení této matematické úlohy. My si samozřejmě ukážeme obě dvě, přičemž poukážeme na jejich silné a slabé stránky.
První přístup je jednodušší, ovšem programová logika nezavádí vyšší míru abstrakce, neboť kvadratickou rovnici nechápe jako abstraktní matematický objekt. Studenti, kteří volí tuto alternativu řešení úlohy, obvykle definují množinu samostatných proměnných, do nichž nejprve uloží vstupní data, což jsou koeficienty kvadratického, lineárního a absolutního členu. Poté zkonstruují další proměnnou, kterou inicializují vypočtenou hodnotou diskriminantu. Jelikož se pohybujeme v množině reálných čísel (R), tak na základě hodnoty diskriminantu můžeme při řešení kvadratické rovnice dospět k následujícím stavům:

1.
Diskriminant (D) je menší než nula (D < 0), což znamená, že v množině R nemá kvadratická rovnice žádné kořeny, a tedy ani žádné řešení.
2.
Diskriminant je rovný nule (D = 0), a kvadratická rovnice má jeden, tzv. dvojnásobný kořen.
3.
Diskriminant je větší než nula (D > 0), čehož praktickou implikací je existence dvou různých reálných kořenů kvadratické rovnice.
Jelikož vidíme, že matematický postup se větví podle hodnoty diskriminantu, tak při algoritmizaci musíme adekvátně zapracovat do programu řízení jeho toku. To se nejčastěji děje pomocí rozhodovacích příkazů. Přirozeně od programu očekáváme, že ať už dojde k jakémukoliv z výše uvedených stavů, vždy podá uživateli finální informaci. Algoritmus pro vyřešení kvadratické rovnice je pomocí vývojového diagramu znázorněn na obrázku. Podívejme se nyní na fragment zdrojového kódu jazyka C# 3.0, jenž nás obeznámí se syntaktickým obrazem 1. řešení zadané matematické úlohy.
Komentář k programu: Jakmile načteme od uživatele požadovaná vstupní data, přistoupíme k jejich transformaci. Diskriminant kvadratické rovnice zjistíme podle známého vztahu. Přestože někteří studenti se snaží aritmetický výraz, jenž je zapsán na pravé straně přiřazovacího příkazu, závorkovat, není to nutné. Jestliže vás napadá otázka proč, pak prosím vězte, že kompilátor dovede tento aritmetický výraz vyhodnotit pomocí standardních pravidel pro prioritu a asociativitu operátorů.
Větvení se provádí prostřednictvím rozhodovacího příkazu if, resp. pomocí vybrané varianty tohoto rozhodovacího příkazu. V našem programu uplatňujeme variantu vícecestného rozhodování příkazem if-else if- else. Rozhodování probíhá takto:
1. Vyhodnotí se podmínkový výraz ve větvi if. Podmínkovým výrazem je v našem případě relační výraz (výrazu říkáme relační proto, že obsahuje binární relační operátor <). Relační výraz diskriminant < 0 je vyhodnocen na logickou pravdu (true) tehdy, je-li hodnota diskriminantu záporná. Pokud je tomu tak, zpracují se všechny příkazy, jež jsou umístěny v těle větve if. Tělo větve if je ohraničeno programovým blokem se složenými závorkami ({}) coby mantinely. Bude-li diskriminant záporný, neexistují žádné reálné kořeny kvadratické rovnice, a proto sdělujeme uživateli, že rovnice nemá v množině R řešení. Poté, co se zpracují všechny příkazy v těle větve if, končí působnost rozhodovacího příkazu if-else if-else a běh programu pokračuje exekucí nejbližšího možného příkazu, jenž následuje za rozhodovacím příkazem.
2. Pokud bude hodnotou podmínkového výrazu větve if logická nepravda (false), znamená to, že diskriminant není záporný (je buď nulový, anebo kladný). Není-li relační výraz splněn, pak v procesu rozhodování přeskakujeme tělo větve if a přesouváme se na větev else if. Zatímco v jednom rozhodovacím příkazu se může větev if objevovat právě jednou, absolutní četnost větví else if není prakticky omezena. Podobně jako větev if, rovněž větev else if musí disponovat podmínkovým výrazem. Podmínkový výraz větve else if testuje, zda je diskriminant nulový. Tomuto testu odpovídá relační výraz diskriminant == 0. Rádi bychom poznamenali, že operátor == je relačním operátorem, přičemž zjišťuje existenci relace mezi svými operandy.
3. Pokud bude diskriminant roven nule, tak podmínkový výraz ve větvi else if bude splněný (jeho hodnotou bude logická pravda). Vzápětí se provedou všechny příkazy, jež jsou přítomny v těle větve else if. Prakticky to znamená, že program vypočte hodnotu dvojnásobného kořene a zobrazí ji na obrazovce počítače.
4. Dobrá, jak ovšem budeme postupovat, když podmínkový výraz ve větvi else if nebude splněn? Jednoduše: bude-li hodnotou podmínkového výrazu logická nepravda, tak víme, že diskriminant není roven nule. Když se v procesu rozhodování dostaneme až sem, můžeme s jistotou říct, že diskriminant není ani záporný, ani nulový. Je-li tomu tak, zbývá nám už pouze jediná možnost: diskriminant musí být kladný a kvadratická rovnice bude mít dva samostatné kořeny. Míjíme tedy tělo větve else if a přesouváme se na větev else.

Přestože o operátorech ještě bude řeč, dovolíme si oba termíny vysvětlit. Priorita determinuje pořadí, v němž budou realizovány operace, které jsou předepsány aplikovanými operátory. Některé operátory jazyka C# 3.0 mají vyšší prioritu než jiné. Kupříkladu ve zmíněném aritmetickém výrazu má operátor součinu (*) vyšší prioritu než operátor rozdílu (–). V tomto směru pracují oba operátory přesně podle matematických pravidel, na něž jsme zvyklí. Čili výraz zapsaný jako b * b – 4 * a *c bude ve skutečnosti vyhodnocen, jako kdyby byl zapsán v následujícím tvaru: (b * b) – (4 * a * c). Pokud studenti programování zapíší aritmetický výraz v této podobě, neudělají žádnou chybu, jenom vizuálně naznačí to, co je pro kompilátor samozřejmé. Bohužel, za jistých okolností nejsou pravidla určující prioritu operátorů schopna jednoznačně rozhodnout, jak bude výraz vyhodnocen. Ještě než začneme s dosazováním hodnot do vzorců, musíme unifikovat jednotky všech veličin. Jelikož budeme chtít mít všechny veličiny zadané v metrech nebo v sekundách, budeme muset počáteční rychlost auta převést z km/h na m/s. To znamená dělit rychlost zadanou v km/h reálnou konstantou 3,6. Platí, že 60 km/h se přibližně rovná 16,67 m/s.

třeba výraz x * y / z. Jak jej kompilátor vyhodnotí? Jako (x * y) / z, nebo jako x * (y / z)? A právě to je ona nejednoznačnost, o níž mluvíme. Kolizní stav vzniká jako důsledek použití operátorů se stejnou prioritou. Oba operátory (* a /) jsou aritmetickými operátory, přičemž sdílejí jednu prioritní třídu. V tomto kontextu potřebujeme další mechanizmus, jenž odstraní jakékoliv nejasnosti ve způsobu, jakým bude aritmetický výraz vyhodnocen. Tímto mechanizmem je asociativita operátorů. Obecně můžeme asociativitu definovat jako pořadí realizace operací, které jsou předepsány operátory se stejnou prioritou. Asocia-tivita je pro snazší pochopení dána směrem, ve kterém jsou zpracovávány operace, jejichž výkon požadují operátory s identickou prioritou. Operátory mohou být asociativní buď ve směru zleva doprava, anebo ve směru zprava doleva. Aritmetické operátory jsou asociativní zleva doprava, z čehož vyplývá, že výraz x * y / z bude vyhodnocen jako (x * y) / z. Většina operátorů disponuje stejnou asociativitou jako zmíněné aritmetické operátory, ovšem najdeme i operátory, jež jsou asociativní zprava doleva (je to například přiřazovací operátor).
Tato větev je poslední v celém rozhodovacím příkazu if-else if-else. Za zmínku jistě stojí, že větev else nespolupracuje s žádným podmínkovým výrazem. Když se nad tím zamyslíme, dojdeme k logickému závěru: Pokud větev else ošetřuje „všechny ostatní případy“, tak by bylo úplně zbytečné používat jakýkoliv podmínkový výraz. Dostane-li se rozhodování do tohoto stavu, tak víme, že existují dva kořeny kvadratické rovnice, ovšem prozatím nevíme, jaké tyto kořeny jsou. Proto zavádíme do těla větve else příkazy pro stanovení kořenů a jejich zobrazení. S výpočtem druhé odmocniny diskriminantu nám pomůže statická metoda Sqrt třídy Math ze jmenného prostoru System. Stejně jako větev if, také větev else se může v jednom rozhodovacím příkazu vyskytovat pouze jedenkrát.
Druhý způsob algoritmizace této matematické úlohy zavádí vyšší míru abstrakce, poněvadž na kvadratickou rovnici nahlíží jako na abstraktní matematický objekt. Jako vývojáři smíme modelovat různé entity, matematické objekty nevyjímaje. V našem podání vytvoříme virtuální reprezentaci kvadratické rovnice pomocí inteligentně deklarované struktury.

Komentář ke kódu: Kvadratickou rovnici bude představovat instance deklarované struktury. Veškerou funkcionalitu jsme přitom zapouzdřili do deklaraci struktury. Struktura obsahuje parametrický konstruktor, jenž je zodpovědný za inicializaci datových členů instance struktury. V nabídce struktury je také veřejná bezparametrická metoda Vyřešit, která disponuje logikou pro nalezení kořenů kvadratické rovnice. A jak probíhá řešení kvadratické rovnice pomocí instancí spřízněné struktury? Velice přirozeně, řekli bychom.
Praktickou aplikací instance struktury jsme malinko zjednodušili, protože formálním parametrům konstruktoru předáváme tři diskrétní celočíselné konstanty (koeficienty členů kvadratické rovnice nezískáváme od uživatele, nýbrž je zadáváme přímo do zdrojového kódu). Variaci programu, která získává data od uživatele, ponecháváme jako další praktický experiment pro čtenáře.


praktické cvičení:
Analýza detekce kolize mezi automobilem a chodcem

Uvažujme tuto modelovou situaci: Automobil jede po cestě, když se v jisté vzdálenosti od něj objeví chodec. Cílem našeho dalšího praktického cvičení je zjistit, zda mezi dopravním prostředkem a člověkem dojde ke kolizi, či nikoliv. Budeme přitom postupovat následovně:

1.
Celkovou vzdálenost, která dělí automobil od chodce, označíme identifikátorem SC. Dodejme, že celková vzdálenost je dána dráhou, která dělí automobil a chodce v čase t0, čili v čase, kdy auto jede počáteční rychlostí v0, a řidič spatří chodce na cestě před sebou.
2.
Při analýze pohybu automobilu rozdělíme celou dráhu na dvě menší dráhy: jedna z nich bude reakční dráha (SR) a druhá bude brzdná dráha (SB). Reakční dráha je dráha, kterou automobil urazí během reakčního času (tR), tedy času, který je vymezen časovým intervalem, na jehož konci si řidič automobilu uvědomí, že v zájmu zabránění kolize s objektem na cestě má sešlápnout brzdový pedál a snížit tak rychlost auta. Brzdná dráha je pak dráha, která je nutná k úplnému zastavení auta. Jelikož počítáme s úplným zastavením, konečná rychlost automobilu (vF) bude nulová.
3.
Problém detekce kolize má fyzikální charakter, přičemž pro jeho vyřešení využijeme několik matematických vztahů z mechaniky, jakožto jedné disciplíny moderní fyziky. Je zřejmé, že ke kolizi nedojde v případě, že SR + SB < SC. Protože reakční dráhu přejde automobil v počáteční rychlosti v0, můžeme ji vypočítat takto:

SR + vo tR
Výpočet brzdné dráhy je o něco komplikovanější, neboť v něm musíme pracovat se zpomalením automobilu v důsledku aktivace brzdných mechanizmů. Všeobecný vzorec pro výpočet brzdné dráhy má tuto podobu:

SB = vF2 - vO2
2a
kde:
vF je konečná rychlost automobilu,
v0 je počáteční rychlost automobilu,
a je koeficient zpomalení.

Jelikož z našich předpokladů vyplývá, že vF = 0, tak samozřejmě i vF2 = 0. Všeobecný vzorec pro stanovení brzdné dráhy proto smíme upravit následujícím způsobem:

SB = - vO2
2a
Co to však ve skutečnosti znamená? Pomozme si praktickou ukázkou. Povězme, že počáteční rychlost automobilu bude 60 km/h, celková vzdálenost (SC) bude 50?m, reakční čas (tR) bude 0,45 s a koeficient zpomalení (a) bude –6,5 m/s2. Reakční a brzdnou dráhu vypočítáme takto:

SR = 16,67 × 0,45 = 7,5 m

SB = -16,672 = -277,89 = 21,38
2× (-6,5) -13
ST = SR × SB = 7,5 × 21,38 = 29 m

V našem fyzikálním modelu tedy automobil zcela zastaví po ujetí 29 metrů, takže ke kolizi s chodem naštěstí nedojde.

Algoritmizace fyzikální úlohy

Komentář ke kódu: Program věrně kopíruje fyzikální výklad, jak jsme ho uvedli, a proto se domníváme, že byste se neměli setkat s žádnými kritickými pasážemi. Jenom připomínáme, že program očekává zadání záporné hodnoty pro koeficient zpomalení. Výstup programu lze pozorovat na obrázku. 8 0716/CZ o

Autor článku