× Aktuálně z oboru

Programátoři po celém světě dnes slaví Den programátorů [ clanek/2018091300-programatori-po-celem-svete-dnes-slavi-den-programatoru/ ]
Celá zprávička [ clanek/2018091300-programatori-po-celem-svete-dnes-slavi-den-programatoru/ ]

C++ - 7. lekce

[ http://programujte.com/profil/1483-lukas-chury/ ]Google [ https://plus.google.com/101446994797551111026?rel=author ]       [ http://programujte.com/profil/20356-josef-vitu/ ]Google [ ?rel=author ]       13. 4. 2005       67 469×

  • Funkce a pole – vytváření
  • Naplnění pole
  • Zobrazení pole a jeho ochrana pomocí const
  • Modifikace pole
  • Sestavení
  • Funkce a struktury
  • Předání a navrácení struktur
  • Příklady
  • Rekurze
  • Úkol č. 7

Lekce č.7

Další lekce je tu, všichni se netrpělivě těšíte na ukazatele (pointery)… Ale o tom příště:) Dnes si povíme o f-cích podrobněji, mnohem podrobněji. Důsledně nastudovat!


7.1 Funkce a pole – vytváření

Dosavadní f-ce byly jednoduché, používaly pouze základní typy parametrů a návratových hodnot. Ale f-ce mohou zacházet s mnohem komplikovanějšími typy – pole, struktury. Abychom vytvořili f-ci obecně, neomezíme pole velikostí, ale předáme jí jako parametr velikost pole:


int sum_pole(int pole[], int n)    
//pole = jméno pole, n = velikost

Tato f-ce nám může posloužit s použitím cyklu na součet prvků pole. Vypadá to jednoduše: hranaté závorky nám říkají, že se jedná o pole (libovolné velikosti). Nenechte se zmást, pole není polem, ale ukazatelem! Ale tím si nezatěžujte hlavu, protože zacházet s ním můžeme jako s polem. Ještě bych vám mohl ukázat kód f-ce pro zmíněný součet prvků:


int sum_pole(int pole[], int n)    
{       
   int celkem = 0;       
   for (int i = 0; i<n ; i++)           
   celkem = celkem + pole[i];
   return celkem;
}

Pamatujte:

Indikaci druhu pole a počet prvků předávejte pomocí dvou samostatných parametrů:


void dobrepole(int pole[], int vel); // prototyp
void spatnepole(int pole [vel]); // NE - nesprávný prototyp

7.11 Naplnění pole

Nyní si napíšeme f-ci, která má dva parametry (jméno pole a specifikaci max. počtu položek, který se má přečíst) a navrací skutečný počet zadaných položek. Jestliže například použijete f-ci s polem o pěti prvcích, předáte 5 jako druhý parametr. Zadáte-li pouze dvě hodnoty, f-ce vrátí 2. K načtení po sobě jdoucích hodnot můžeme použít cyklus. Jak ale ukončíme vstup (když nechceme zadat již další hodnotu)? Uděláme to tak, že pokud uživatel zadá zápornou hodnotu, bude to znamenat signál pro ukončení.


int pocpole(double pole[], int limit)
{
   double temp;
   int i;
   for (i=0 ; i<limit ; i++)
   {
      cout << "zadejte hodnotu #" << i+1 << ":";
      cin >> temp;
      if (temp < 0) //signál pro ukončení
      break; // vysvětlení najdete níže u cyklu CASE
      pole[i] = temp;
   }
   return i;

}

Pokud uživatel zavede nezápornou hodnotu, přiřadí se do pole, jinak cyklus končí. Pokud přiřadí pouze platné hodnoty, cyklus končí po naplnění maximálního počtu prvků i. F-ce poté tuto hodnotu navrací.

7.12 Zobrazení pole a jeho ochrana pomocí const

Pokud účelem f-ce není měnit data, která se jí předávají, měli byste to zabezpečit. C++ automaticky zabezpečuje obvyklé parametry, protože je předává hodnotou a f-ce pracuje s jeho kopií. F-ce pracující s polem pracují s originálem. Abychom zabránili změně parametrů obsahů pole, můžeme použít již známé const:

   
void ukazpole(const double novepole[], int n);

Deklarace stanoví, že se odkazuje na konstantní data. Můžete použít novepole[0], ale nemůžete je změnit. Pozor! To neznamená, že musí být původní pole konstantou, ale že nemůžete použít novepole ve f-ci ukazpole na změnu dat. F-ce tedy zachází s polem jako s daty pouze pro čtení. Nezapomeňte, nyní pracujeme se ZOBRAZENÍM pole:

 
void ukazpole(const double novepole[], int n)  
{      
   for (int i = 0; i<n ; i++)        
   {           
      cout << "Majetek #" << i+1 << ": Kc: ";           
      cout << novepole[i] << "\n";      
   }   
}

7.13 Modifikace pole

Další operace pro naše pole je násobení (např.) každého prvku pole určitým koeficientem. Prostě modifikace prvků pole. Potřebujeme k tomu tři parametry: koeficient, pole a počet prvků pole. Nejsou potřeba žádné návratové hodnoty.

   
void zmenapole(double r, double pole[], int n)  
{       
   for (int i = 0; i<n ; i++)           
   pole[i] *= r;        // neboli   
   pole[i] = pole[i] * r   
}

Protože se od f-ce předpokládá, že bude měnit hodnoty, nepoužívejte const, když deklarujete pole.

7.14 Sestavení

Nyní příklad, který to vše sumarizuje (všimněte si, že ve f-ci main je čím dál méně příkazů). Následující příklad uloží do pole o pěti prvcích pět hodnot a ty následně zvětší o zadaný koeficient:


#include <iostream>
using namespace std;
const int Max = 5;
// prototypy funkcí
int fill_array(double ar[], int limit);
void ukaz_array(const double ar[], int n); // nemění data
void revalue(double r, double ar[], int n);
int main()
{
   double properties[Max];
   int size = fill_array(properties, Max);
   ukaz_array(properties, size);
   cout << "Zadejte koeficient prehodnoceni: ";
   double factor;
   cin >> factor;
   revalue(factor, properties, size);
   ukaz_array(properties, size);
   cout << "Hotovo.\n";
       
   cin.get();       
   cin.get();
   return 0;
}

int fill_array(double ar[], int limit) // naplnění pole
{
   double temp;
   int i;
   for (i = 0; i < limit; i++)
   {
      cout << "Zadejte hodnotu c." << (i + 1) << ": ";
      cin >> temp;

      if (!cin) // neplatný vstup  
      {
         cin.clear();
         while (cin.get() != '\n')       
         continue;   
         cout << "Neplatny vstup, zadavani ukonceno.\n";    
         break;  
      }             
      else if (temp < 0) // signál ukončení zadávání
         break;
      ar[i] = temp;
   }

   return i;
}
// následující funkce může používat, ale ne měnit
// pole, jehož adresa je ar

void ukaz_array(const double ar[], int n) // čtení pole
{
   for (int i = 0; i < n; i++)
   {
      cout << "Majetek c." << (i + 1) << ": ";
      cout << ar[i] << " Kc\n";
   }
}

// násobí každý prvek pole ar[] koeficientem r

void revalue(double r, double ar[], int n) // úprava všech prvků pole
{
   for (int i = 0; i < n; i++)
   ar[i] *= r;
}

Výsledek:

Zadejte hodnotu c.2: 80000
Zadejte hodnotu c.3: 222000
Zadejte hodnotu c.4: 240000
Zadejte hodnotu c.5: 118000
Majetek c.1: 100000 Kc
Majetek c.2: 80000 Kc
Majetek c.3: 222000 Kc
Majetek c.4: 240000 Kc
Majetek c.5: 118000 Kc
Zadejte koeficient prehodnoceni: 1.10
Majetek c.1: 110000 Kc
Majetek c.2: 88000 Kc
Majetek c.3: 244200 Kc
Majetek c.4: 264000 Kc
Majetek c.5: 129800 Kc
Hotovo.

…nebo v případě špatného vstupu:

Zadejte hodnotu c.1: 200000
Zadejte hodnotu c.2: 84000
Zadejte hodnotu c.3: 160000
Zadejte hodnotu c.4: -2
Majetek c.1: 200000 Kc
Majetek c.2: 84000 Kc
Majetek c.3: 222000 Kc
Zadejte koeficient prehodnoceni: 1.20
Majetek c.1: 240000 Kc
Majetek c.2: 100800 Kc
Majetek c.3: 192000 Kc
Hotovo.


7.2 - Funkce a struktury

Napsat f-ci pro strukturu je jednodušší než pro pole. Struktury můžete předat hodnotou – funkce pracuje s kopií původní struktury. Funkce může také vrátit strukturu. Nejlepší je předávat je jako parametry a, pokud je to nezbytné, použít jako návratové hodnoty. Pokud zvolíme předávání struktury hodnotou, je zde jedna nevýhoda. Pokud je struktura příliš velká, úsilí spojené s vytvořením kopie zvýší požadavky na paměť a zpomalí systém. Proto mnoho programátorů v C dává přednost předání struktury adresou a potom pro přístup k jejímu obsahu použít ukazatel. C++ poskytuje třetí alternativu – předání odkazem, to probereme jindy.

7.21 Předání a navrácení struktur

Předání struktur hodnotou má největší smysl. Předpokládejme, že chceme spočítat časy cestování. Definování struktury je jednoduché:


struct cestovani
{   
   int hodiny;   
   int minuty;
};

Dále vytvoříme prototyp f-ce sum, která navrací součet dvou takových struktur. Návratová hodnota by měla být typu cestovani, a tudíž by měla mít dva parametry:

   
cestovani sum(cestovani t1, cestovani t2);

Abychom sečetli dva časy, nejprve sečteme minutové členy, díky celočíselnému dělení 60-ti získáme počet hodin a operátor modulo % poskytne počet zbývajících minut:


#include <iostream>
using namespace std;
struct travel_time
{
   int hours;
   int mins;
};

const int Mins_per_hr = 60;

travel_time sum(travel_time t1, travel_time t2);
void ukaz_time(travel_time t);

int main()
{
   travel_time day1 = {5, 45}; // 5 hodin, 45 min
   travel_time day2 = {4, 55}; // 4 hodiny, 55 min

   travel_time trip = sum(day1, day2);
   cout << "Celkem za dva dny: ";
   ukaz_time(trip);

   travel_time day3 = {4, 32};
   cout << "Celkem za tri dny: ";
   ukaz_time(sum(trip, day3));

   return 0;
}

travel_time sum(travel_time t1, travel_time t2)
{
   travel_time total;

   total.mins = (t1.mins + t2.mins) % Mins_per_hr;
   total.hours = t1.hours + t2.hours + (t1.mins + t2.mins) / Mins_per_hr;
   return total;
}

void ukaz_time(travel_time t)
{
   cout << t.hours << " hodin, " << t.mins << " minut\n";
}

Zde travel_time funguje jako standardní jméno typu; můžete ho použít na deklaraci proměnných, funkčních návratových typů a typů parametru f-ce.

Celkem za dva dny: 10 hodin, 40 minut
Celkem za tri dny: 15 hodin, 12 minut

7.22 Příklady

Další příklad se zabývá prostorem, místo času.

Předpokládejme, že chceme popsat pozici bodu na mapě vzhledem k nějakému počátku. Tradičně X pro horizontální posunutí a Y pro vertikální. Jeden způsob je stanovit posunutí jak horiz., tak vertik. bodu od počátku.


struct sourad
{            
   double x;          
   double y;
};

Druhý způsob popisu pozice bodu je stanovit, jak daleko je od počátku a ve kterém je směru (např. 40 stupňů severně od počátku):


struct uhel
{           
   double pocat;          
   double uhel;
};

F-ce zobrazující obsah struktury bod2. Matematické knihovny v C++ počítají s úhly v radiánech, ale pro zobrazovací údaje je konvertujeme na stupně (násobením 180/(pi), což je přibližně 57.29577951). Zde je:


void ukaz_sour(uhel prvni) // f-ce se jmenuje ukaz_sour a jako parametr předáváme strukturu prvni
{
   const double rad_na_stupne = 57.29577951;
   cout << "vzdalenost = " << prvni.pocat;
   cout << ",uhel = " << prvni.uhel * rad_na_stupne;
   cout << " stupnu\n";
}

Všimněte si, že formální proměnná je typu uhel. Když předáte f-ci strukturu uhel, obsah se kopíruje do struktury prvni a f-ce potom pracuje s touto kopií. Protože je prvni struktura, f-ce používá k identifikaci členů struktury operátor příslušnosti . (tečku). Podívejme se dál a napišme f-ci, která konvertuje pravoúhlé souřadnice na polární (v našich strukturách sourad na uhel). Pro volání sqrt potřebujeme knihovnu cmath (na starších systémech překvapivě math.h).


pocat = sqrt (x * x + y * y)        
//neboli pocat = odmocnina z (x nadruhou + y na druhou)
// pythagorova věta

F-ce atan2() z matematické knihovny počítá z hodnot xy úhel:


uhel = atan2(y,x)

…(existuje f-ce atan(), ale ta nerozlišuje mezi úhly 180° odděleně).

Jsou dány vzorce, tady je f-ce, která konvertuje pravoúhlé souřadnice na polární:


uhel sourad_na_uhel(sourad xypos) // typ uhel
{
   uhel prenos;
   prenos.pocat = sqrt(xypos.x * xypos.x + xypos.y + xypos.y);
   prenos.uhel = atan2(xypos.y, xypos.x);
   return prenos;
}

No a nyní zbytek programu:


#include <iostream>
#include <cmath>
using namespace std;

// šablony struktur struct uhel
{
   double pocat; // vzdálenost od počátku
   double uhel; // směr od počátku
};
struct sourad
{
   double x; // horizontální vzdálenost od počátku
   double y; // vertikální vzdálenost od počátku
};

// prototypy
uhel sourad_to_uhel(sourad xypos);
void ukaz_uhel(uhel prvni);

int main()
{
   sourad rplace;
   uhel pplace;

   cout << "Zadejte hodnoty x a y: ";
   while (cin >> rplace.x >> rplace.y) // úhledné použití cin
   {
      pplace = sourad_to_uhel(rplace);
      ukaz_uhel(pplace);
      cout << "Dalsi dve cisla (q na ukoceni): ";
   }
   cout << "Hotovo!\n";
   return 0;
}

// konvertuje pravoúhlé souřadnice na polární
uhel sourad_to_uhel(sourad xypos)
{
   uhel pristup;
   pristup.pocat = sqrt( xypos.x * xypos.x + xypos.y * xypos.y);
   pristup.uhel = atan2(xypos.y, xypos.x);
   return pristup; // vrací strukturu uhel
}

// zobrazení polárních souřadnic spolu s konverzí úhlu na stupně
void ukaz_uhel (uhel prvni)
{
   const double Rad_to_deg = 57.29577951;
   cout << "vzdalenost = " << prvni.pocat;
   cout << ", uhel = " << prvni.uhel * Rad_to_deg;
   cout << " stupnu\n";
}

Zadejte hodnoty x a y: 30 40
vzdalenost = 50, uhel = 53.1301 stupnu
Dalsi dve cisla (q na ukoceni): -100 100
vzdalenost = 141.421, uhel = 135 stupnu
Dalsi dve cisla (q na ukoceni): q
Hotovo!

Poznámky k programu:


(cin >> rplace.x >> rplace.y)

…je to samé jako:


cin >> rplace.x;
cin >> rplace.y;

7.3 Rekurze

F-ce v C++ mají zajímavou vlastnost – můžou volat samy sebe (kromě f-ce main()). Rekurze je důležitá například pro programování umělé inteligence. Když funkce rekurzním voláním volá sama sebe, pak nově vyvolaná f-ce volá opět sama sebe a tak dále, dokud kód neobsahuje něco na ukončení tohoto řetězce volání. Obvykle se dělá rekurzivní volání příkazu if:


void nazev_fce(seznamparametrů)
{
   příkazy1
   if (test)
   nazev_fce(příkazy) // tady se volá f-ce sama sebe
   příkazy2

Díky štěstí či prozíravosti se test nakonec stane nepravdivý a řetěz volání se přeruší. Jestliže nazev_fce podstoupí pět rekurzivních volání, nejprve se provede sekce příkazy1 5× v pořadí, ve kterém se volaly a potom se provede 5× sekce příkazy2 v opačném pořadí, než byly f-ce volány. Po proběhnutí pěti úrovní rekurze potom program musí vycouvat přes pět stejných úrovní.


#include <iostream>
using namespace std;
void odpocet(int n);

int main()
{
   odpocet(4); // volá rekurzivní funkci
   return 0;
}

void odpocet(int n)
{
   cout << "Pocitani smerem dolu... " << n << "\n";
   if (n > 0)
   odpocet(n-1); // funkce volá sama sebe
   cout << n << ": Kaboom!\n";
}

Pocitani smerem dolu ... 4 " úroveň 1 -- začátek přidávání úrovní rekurze
Pocitani smerem dolu ... 3 " úroveň 2
Pocitani smerem dolu ... 2 " úroveň 3
Pocitani smerem dolu ... 1 " úroveň 4
Pocitani smerem dolu ... 0 " úroveň 5 -- poslední rekurzivní volání
0: Kaboom! " úroveň 5 -- začátek návratu přes posloupnost volání
1: Kaboom! " úroveň 4
2: Kaboom! " úroveň 3
3: Kaboom! " úroveň 2
4: Kaboom! " úroveň 1

Víc proniknout do f-cí nemůžeme, poněvadž zbytek f-cí a jejich možností se váže na ukazatele. Ty probereme zřejmě již v další lekci. Ale možná přijde taková vsuvka v podobě zpětného rozšíření probraného učiva, které je neméně potřebné.


7.4 - Úkol

Vytvořte katalog: Uživatel bude zadávat ceny položek (tedy program se zeptá na první položku, druhou, atd.) a až nebude již chtít uživatel zadat další cenu pro další položku, bude tam něco na ukončení zadávání (např. „q“), vymyslete nějaký zajímavý způsob. Kompletně bude program obsahovat češtinu a znaky ASCII (nějaké orámování, podtrhnutí…). Poté program vypíše všechny položky a jejich ceny a dá uživateli možnost editovat kteroukoliv položku (program se zeptá, zda chce uživatel upravit nějakou položku, pokud ano, zadá číslo položky a napíše novou cenu). Poté se program zeptá, zda chce ke všem položkám připočíst DPH (19 %) pokud ano, všechny ceny se upraví samy dle DPH, pokud ne, ceny zůstanou. Poté se provede znovu výpis položek a jejich cen. Následně program vypočítá celkovou cenu všech položek. Nakonec, poté co uživatel odsouhlasí konečnou cenu, se spustí odpočítávání ukončení programu (max. 5).


Článek stažen z webu Programujte.com [ http://programujte.com/clanek/2005041411-c-7-lekce/ ].