Object Pascal - 04: Pokračování v OOP
 x   TIP: Přetáhni ikonu na hlavní panel pro připnutí webu
Reklama

Object Pascal - 04: Pokračování v OOPObject Pascal - 04: Pokračování v OOP

 

Object Pascal - 04: Pokračování v OOP

Google       Google       12. 4. 2009       14 162×

Vítejte ve čtvrtém dílu malého seriálu o jazyku Object Pascal určeném pro začátečníky. V tomto dílu budeme pokračovat v úvodu k objektově orientovanému programování (OOP), protože OOP by dnes do základů programování jistě patřit mělo. Po úvodní teorii z minulého dílu konečně dojde i na program v Pascalu. Nejdřív ale přijde opět malé opakování pojmů z minulého dílu. Další, už třetí část povídání o datových typech v Pascalu, tedy "Typy 3. část", nechám ale až na jindy. Přeji vám pohodu při čtení a práci!

Reklama
Reklama

Čtvrtý díl seriálu je rozdělen do dvou kapitol: v první připomenu úlohy z minula a ve druhé kapitole budeme pokračovat v úvodu do pojmů OOP. Pokud tyto řádky čtete, minulé povídání o OOP vás asi neodradilo. Časem zjistíte, že je to jiný a hlavně příjemnější styl práce. Půjde asi zase o delší povídání, tak si udělejte dobrý čaj, kafe (v angličtině hovorově javu, ale my zde programujeme v Pascalu) apod. a snažte se vydržet. Nedávno jsem v rádiu (Vltava, ČR 3) slyšel o pití čaje a kávy (javy) krásnou písničku skupiny Ink spots s názvem Java jive (1940), mohla by vás při práci potěšit. Najděte si ji na netu, popř. stáhněte na svo.xf.cz.

1. kapitola: opakování, řešení úloh z 3. dílu

Datové typy

Z 2. a 3. dílu víme, že každá proměnná v Pascalu musí mít určeny tři vlastnosti:

  1. velikost proměnné (počet bytů), aby si program mohl pro proměnnou na začátku rezervovat vhodné místo v paměti,
  2. přípustné hodnoty (např. celá čísla, desetinná čísla, znaky, řetězce znaků atd.),
  3. přípustné operace, které lze s danou proměnnou provádět (např. násobení, dělení, nalezení bezprostředního následníka, nalezení bezprostředního předchůdce, výpočet odmocniny…).

V minulém dílu jsme se zabývali třetí vlastností: přípustnými operacemi, a to u typů integer, char a real.

K operacím s typem integer byly zadány 4 úkoly:

  • Úkol 1: Napište program, který po zadání čísla vypíše, jestli je zadané číslo liché, nebo sudé.
  • Úkol 2: Napište program, který po zadání čísla vypíše, jestli je zadané číslo dělitelné třemi.
  • Úkol 3: Napište program, který po zadání čísla vypíše, jestli je zadané číslo dělitelné třemi, a pokud není dělitelné třemi, vypíše zbytek při tomto dělení.
  • Úkol 4: Napište program, který požádá uživatele o zadání dvou čísel - první bude dělitel a druhé dělenec. Program poté vypíše, jestli je druhé zadané číslo dělitelné prvním (dělitelem), popř. vypíše i zbytek při dělení, pokud číslo dělitelem dělitelné není.

Podívejme se na možné řešení posledního z nich, který je nejobecnější. Vložím do něj ale i řešení úkolu 1. Všimněte si zde možnosti zkráceného, ale správného zápisu podmínky (lze vynechat dokončení " = true ", 18. řádek).

program deleniInteger;
{$APPTYPE CONSOLE}
uses
  SysUtils;
var delenec, delitel, vysledek, zbytek: integer;
  // vysledek bychom mohli nazvat take podíl
begin
  // zadani dvou cisel:
  write('Zadej prvni cele cislo (delenec): ');
  readLn(delenec);
  write('Zadej druhe cele cislo (delitel): ');
  readLn(delitel);
  // jsou cisla suda/licha? (ukol 1)
  writeLn; // vynechame jeden radek
  if odd(delenec) = true  // uplny zapis podminky
    then writeLn('1. cislo je liche.')
    else writeLn('1. cislo je sude.');
  if odd(delitel)     // !!! zkraceny, ale take spravny zapis podminky!
    then writeLn('2. cislo je liche.')
    else writeLn('2. cislo je sude.');
  // zkouska delitelnosti: delenec / delitel
  writeLn; // vynechame jeden radek
  writeLn('Zkouska delitelnosti cisel.');
  writeLn;
  vysledek := delenec div delitel;
  writeLn('Druhe cislo je obsazeno v prvnim ',
    vysledek, ' krat');
  if (delenec mod delitel = 0)
    then writeLn('Prvni cislo je delitelne druhym.')
      // pozor, za radkem then neni strednik,
      // jeste zde nekonci cely podmineny prikaz
    else
      begin
        writeLn('Prvni cislo neni delitelne druhym.');
        zbytek := delenec mod delitel;
        writeLn('Zbytek pri deleni je ', zbytek);
      end;
  readLn; // pozastaveni programu kvuli vypisu
end.

K operacím s typem real byly zadány 3 úkoly:

  • Úkol 5: Sestavte program, který po zadání dvou celých čísel (typu integer) přiřadí jejich součet, součin a podíl do nových proměnných s názvy soucet, soucin, podil (promyslete jejich typy) a vypíše je.
  • Úkol 6: Upravte program tak, aby u podílu čísel spočetl také jeho celočíselnou část a zaokrouhlenou hodnotu, přiřadil je do proměnných celaCast, zaokrouhleno a opět vypsal.
  • Úkol 7: Napište program, který vypíše hodnoty funkce sinus, kosinus a tangens v intervalu 0° až 90° po 1°. Zjistíte jistě brzy, že funkce sin a cos počítají pouze s úhly v jednotce ... (dokončení v odpovědi č. 2 na konci lekce).

V úkolu 5 by proměnné soucet a soucin mohly být typu integer, ale podil jistě typu real.

Uvedu možné řešení úkolu 7, kde bude použit cyklus for, se kterým jsme se setkali už v minulých příkladech. Úhly ve stupních bude nutné před použitím goniometrických funkcí převést na radiány, buď to zařídíme sami takto:

  uhelRad := uhelDeg * pi / 180;

Nebo pohledáme v nápovědě Delphi a najdeme k tomu určenou funkci DegToRad. V nápovědě se o ní píše, že je uložena v jednotce Math, takže abychom tuto funkci Delphi mohli v našem programu použít, musíme k němu jednotku Math přiložit. To se zařídí v přípravné části programu v příkazu uses, do kterého novou jednotku připíšeme ke standardně přiložené jednotce SysUtils, takže tato část zdrojového kódu bude vypadat takto:

  uses
    SysUtils, Math;

Samotná funkce je v nápovědě uvedená pro nás zatím složitějším zápisem:

  function DegToRad(const Degrees: Extended): Extended;

Ten říká, že se jedná o funkci s názvem DegToRad, s povinným parametrem typu Extended (varianta real) a její výstupní hodnota, kterou vrátí funkce do programu, bude téhož typu. Parametr je zde nazván Degrees, ale my si ho v programu můžeme nazvat jinak. Při použití v našem programu také uvedené typy už nepíšeme, ty jsou zde uvedeny, protože se jedná o zápis definice funkce. V programu by se tedy mohl vyskytnout při použití této funkce reálně takovýto zápis:

  uhelRad := DegToRad(uhelDeg);

Z nápovědy Delphi také zjistíte, že třeba funkce sinus se značí sin a že se vyskytuje v jednotce System. Tuto jednotku nemusíme k programu připojovat, to dělá Delphi automaticky ke každému programu za nás, protože jednotka System obsahuje skutečně základní funkce a procedury, například naše známé writeLn a readLn!

Naproti tomu funkce tangens (tan) je už trochu něco navíc a je tedy uložena v jednotce Math. No a teď už samotný program:

program goniomFunkce07;
{$APPTYPE CONSOLE}
uses
  SysUtils, Math;
var uhelDeg, cisloTabulky, pocetSloupcu, dolniMezCyklu, horniMezCyklu: integer;
  uhelRad: real;
begin
  writeLn('Program pro vypis hodnot goniometrickych funkci.');
  // cyklus, ktery zajisti postupny vypis kratsich radku, at se hodnoty
    // vypisuji pekne pod sebe
  pocetSloupcu := 7; // 7 sloupcu v tabulce na jeden radek
  for cisloTabulky := 1 to 13 do // 13 krat 7 = 91, dostaneme se do 90 stupnu
    begin
      dolniMezCyklu := (cisloTabulky - 1) * pocetSloupcu;
      horniMezCyklu := cisloTabulky * pocetSloupcu;
      // cyklus pro vypis hlavicky: hodnot uhlu. Tabulator pro oddeleni
        // se zapise znakem 9 z ASCII kodu
      write('Uhel:', #9); // zacatek radku
      for uhelDeg := dolniMezCyklu to horniMezCyklu do
        begin
          write(uhelDeg, #9);
        end;
      writeLn; // odradkovani
      // vypis radku s hodnotymi sinu
      write('Sin:', #9); // zacatek radku
      for uhelDeg := dolniMezCyklu to horniMezCyklu do
        begin
          uhelRad := DegToRad(uhelDeg);
          write(sin(uhelRad):1:3, #9);
        end;
      writeLn; // odradkovani
      // vypis radku s hodnotymi cosinu
      write('Cos:', #9); // zacatek radku
      for uhelDeg := dolniMezCyklu to horniMezCyklu do
        begin
          uhelRad := DegToRad(uhelDeg);
          write(cos(uhelRad):1:3, #9);
        end;
      writeLn; // odradkovani
      // vypis radku s hodnotymi tangenty
      write('Tan:', #9); // zacatek radku
      for uhelDeg := dolniMezCyklu to horniMezCyklu do
        begin
          uhelRad := DegToRad(uhelDeg);
          write(tan(uhelRad):1:3, #9);
        end;
      writeLn; // odradkovani
      writeLn; // odradkovani
    end;
  readLn;
end.  

Tento program není moc zajímavý: prostředí konzoly je zastaralé a hodnoty funkcí v tabulce nás asi moc neoslní.

Konzolu ovšem časem vyměníme za formuláře, které nám Delphi nabízí především a velmi pěkně (začátečníka by ale práce s formuláři asi jen pletla, takto se můžeme soustředit na samotné programování v Pascalu. Přitom přechod na konzoli nebude časem žádným problémem.), proto také myslím nemá smysl moc se zabývat různým "vylepšováním" výpisů, já jsem použil tabelátor (#9), ale nijak víc bych se tím nezabýval a ani vám to nedoporučuji.

A nakonec i z hodnot funkcí se přece jen dá něco zajímavého vyčíst jak z pohledu matematiky, tak programování. Zkuste to (nápověda je na konci tototo dílu).

Další úkoly z minula už nechám bez řešení, pokud někdo chcete, spíš mi pošlete řešení vaše, aspoň uvidím, že kurz někdo čte a i sám programuje :-).

2. kapitola: pokračování v OOP

Opakování úvodu z minulého dílu

  • Snažil jsem se vysvětlit, proč používat OOP místo už zastaralého procedurálního programování.
  • Uvedli jsme si základní vlastnosti OOP (většinou se uvádí: zapouzdření - encapsulation, dědičnost - inheritance, mnohotvarost - polymorfismus).
  • Podrobněji jsme rozebírali zapouzdření (data či vlastnosti nebo atributy společně s metodami neboli schopnostmi, přístupová práva public a private).
  • Dalším základním pojmem je dvojice třída (class, jakási šablona) a instance třídy (často je také nazývána objekt). Názvy tříd v Delphi začínáme písmenem T.
  • Tečková notace při výběru metod či atributů třídy nebo objektu (název_třídy.název_metody, název_třídy.název_atributu, totéž pro objekty).

Zadaný úkol

  • Začali jsme chystat prográmek, který by řešil lineární rovnici a*x + b = 0 (čili při vstupních hodnotách - číslech a, b našel výstupní hodnotu - číslo x).
  • Máme za sebou úvodní, ale velmi důležitou, fázi řešení (je myslím dobré si na nutnost této fáze zvyknout, pokud se chceme vyhnout problémům s pracnými opravami během programování a hlavně nebezpečí toho, že vytvoříme program, který zadavateli nebude vyhovovat), a to návrh třídy, vycházející z analýzy problému.
  • Výsledkem byl následující obrázek (schéma, vycházející z jazyka UML):
    TLinRovnice
    - a: real
    - b: real
    - x: real
    - function najdiKoren(): real
    + constructor vytvorRovnici()
    + procedure zadejKoeficienty()
    + procedure vypisReseni()
  • A čeká nás konečně programování, tedy: fáze implementace.

Navrhuji následující řešení úlohy. Pročtěte si je, snažte se spojit si je s předcházející teorií a sami také spustit v Delphi.

Užitečnou metodou, kterou jsem zde použil, je tzv. konstruktor. Je vyvolána při vytvoření objektu ve vlastním programu příkazem

rovnice := TLinRovnice.vytvorRovnici;
a její úlohou je nejen vytvořit objekt, ale i nastavit potřebné hodnoty jeho atributů (proměnných). Zde to není nutné, ale chtěl jsem na to upozornit.

Tak se pusťte do studia:

program ProjectOOP1;

{$APPTYPE CONSOLE}

uses
  SysUtils;

type

// zde zacina priprava na vlastni program: definice tridy TLinRovnice,
  // a potom podrobne jejich metod, neboli funkci, procedur a popr. konstruktoru

TLinRovnice = class // definice tridy
  private
    a, b: real;
    x: real;
    function najdiKoren: real;
  public
    constructor vytvorRovnici();
    procedure zadejKoeficienty();
    procedure vypisReseni();
end;

var rovnice: TLinRovnice; // deklarace instance - objektu tridy TLinRovnice

// deklarace jednotlivych metod tridy;
  // volim poradi nejdriv soukrome metody - zde funkce, pak konstruktor
  // a dalsi verejne metody - zde procedury

function TLinRovnice.najdiKoren: real; 
  var x: real; // lokalni promenna funkce
  begin
    // vypocte koren, pokud existuje; pokud koren neexistuje,
      // vrati pomocne cislo 0.112358132134
      // rovnice ax+b=0 muze mit jen koren x=-b/a
    if self.a <> 0
      then
        x := -b / a
      else
        x := 0.112358132134;
    result := x; // navratova hodnota z funkce
  end;

constructor TLinRovnice.vytvorRovnici();
  begin  // priradi koeficientum jednoduche pocatecni hodnoty
    self.a := 1;
    self.b := 0;
  end;

procedure TLinRovnice.zadejKoeficienty();
  begin
    write('Zadej koeficient a rovnice ax + b = 0: ');
    readLn(self.a);
    write('Zadej koeficient b rovnice ax + b = 0: ');
    readLn(self.b);
    self.x := self.najdiKoren(); // rovnou se vyresi rovnice
  end;

procedure TLinRovnice.vypisReseni();
  begin
    writeLn; // vynecha jeden radek
    if self.x = 0.112358132134
      then
        writeLn('Rovnice nema reseni!')
      else
        begin
          write('Rovnice ', self.a:3:3, '*x + ', self.b:3:3);
          write(' = 0 ma reseni ', x:3:3);
        end;
  end;
  
// Uf! Pripravna faze byla narocna.

begin // tady teprve zacina vlastni program, ale jak je krasne jednoduchy!
  rovnice := TLinRovnice.vytvorRovnici;
  rovnice.zadejKoeficienty;
  rovnice.vypisReseni;
  readLn; // pozastaveni programu kvuli vypisum
end.

Pozor: u takové malé úlohy je použití třídy, metod a objektu ve vlastním programu trochu jako pověstné střílení kanónem na vrabce! Vždyť stejnou úlohu jsme řešili na začátku tohoto kurzu Object Pascalu, a to daleko jednodušším programem (toto řešení bylo v druhém dílu):

program linearniRovnice;
{$APPTYPE CONSOLE} // direktiva Delphi pro konzolu
uses SysUtils;  // přiložená knihovna Delphi
var a,b,x: real;  // deklarace proměnných
begin
  writeLn('Zadejte hodnoty koeficientu rovnice ax+b=0:');
  write('  a: '); readLn(a);
  write('  b: '); readLn(b);
  if a=0
    then
      begin
        if b=0
          then writeLn('Tato rovnice ma nekonecne mnoho korenu')
          else writeLn('Tato rovnice nema zadny koren');
      end
    else
      begin
        x:=-b/a;
        writeLn('Rovnice ma koren' ,x:2:3);
      end;
  readLn;
end.

Ale proč nezačít s co nejjednodušším problémem? Možná i tak někomu chvíli potrvá, než se v programu pořádně zorientuje. A co je hlavní: u složitých problémů je objektově orientované programování rozhodně efektivnější, jednodušší a hlavně z hlediska možnosti dalších úprav či znovupoužití kódu výhodnější!

Úkol na příště

Zkuste napsat objektově program pro řešení kvadratické rovnice. Doporučuji ale nezačít hned programovat, nýbrž:

  1. Provést podobnou analýzu a návrh třídy (atributů a metod) jako v minulém dílu, tentokrát pro úlohu řešení kvadratické rovnice.
  2. Teprve pak úkol naprogramovat (implementovat).

Tento úkol je myslím pro začátečníka dost těžký. Za jeho i nedokonalé řešení by můj student dostal dvě velké jedničky (analýza + návrh, program). Držím vám palce a těším se na případné řešení či ohlas.

Řešení otázky z tohoto dílu:

  • Z pohledu matematiky ve výstupech programu s hodnotami goniometrických funkcí pěkně vidíme, že funkce sinus a tangens mají pro malé úhly prakticky stejné hodnoty. Při přesnosti na 3 desetinná místa to platí až do 6°. Tato vlastnost se např. ve fyzice dá mnohde využít.
  • Z pohledu programování si všimněte, že i když tg(90°) není definován, náš program ho vypíše (mně vypsal číslo se 17 místy). To je dáno zřejmě tím, že při převodu na radiány z 90° nevypočtou Delphi číslo pí/2 zcela přesně, jinak by u tohoto úhlu nemohl být tangens určen, dokonce by program měl skončit chybou (dělit nulou není možné).

To už je dnes vše. Přeji vám vše dobré, ať se vám daří nejen v programování.

×Odeslání článku na tvůj Kindle

Zadej svůj Kindle e-mail a my ti pošleme článek na tvůj Kindle.
Musíš mít povolený příjem obsahu do svého Kindle z naší e-mailové adresy kindle@programujte.com.

E-mailová adresa (např. novak@kindle.com):

TIP: Pokud chceš dostávat naše články každé ráno do svého Kindle, koukni do sekce Články do Kindle.

2 názory  —  2 nové  
Hlasování bylo ukončeno    
0 hlasů
Google
autor je středoškolským profesorem matematiky, fyziky a informatiky na Gymnáziu ve Frýdlantu nad Ostravicí.
Web    

Nové články

Obrázek ke článku Hackerský kongres přiveze v září do Prahy špičky světové kryptoanarchie

Hackerský kongres přiveze v září do Prahy špičky světové kryptoanarchie

Hackerský kongres HCPP16 pořádá od 30. září do 2. října nezisková organizace Paralelní Polis již potřetí, a to ve stejnojmenném bitcoinovém prostoru v pražských Holešovicích. Letos přiveze na třídenní konferenci přes 40 většinou zahraničních speakerů – lídrů z oblastí technologií, decentralizované ekonomiky, politických umění a aktivismu. Náměty jejich přednášek budou také hacking, kryptoměny, věda, svoboda nebo kryptoanarchie.

Reklama
Reklama
Obrázek ke článku ICT PRO školení zaměřené nejenom na ICT

ICT PRO školení zaměřené nejenom na ICT

Dovolte, abychom se představili. Jsme zaměstnanci společnosti ICT Pro, profesionálové v oblasti poskytování komplexních ICT služeb. Neboli služeb spojených s informačními a komunikačními technologiemi, které dnes - ve 21. století - tvoří  nedílnou součást běžného provozu všech moderních firem.

loadingtransparent (function() { var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true; po.src = 'https://apis.google.com/js/plusone.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s); })();
Hostujeme u Českého hostingu       ISSN 1801-1586       ⇡ Nahoru Webtea.cz logo © 20032016 Programujte.com
Zasadilo a pěstuje Webtea.cz, šéfredaktor Lukáš Churý