Vissza az előzőleg látogatott oldalra (nem elérhető funkció)Vissza a modul kezdőlapjáraUgrás a tananyag előző oldalára (16)Ugrás a tananyag következő oldalára (teszt)Fogalom megjelenítés (nem elérhető funckió)Fogalmak listája (nem elérhető funkció)Oldal nyomtatása (nem elérhető funkció)Oldaltérkép megtekintéseSúgó megtekintése

Tanulási útmutató

Összefoglalás

Függvények használata; tömbök függvényekben; a korábbi programozási tételes feladatok újrafogalmazása függvényekkel.

Követelmény

Önállóan megoldható feladatok

  • Fájl letöltése: Az elemei tételeket gyakoroltató feladatsor. című háttéranyag letöltése
  • Információ az állományról: Az elemei tételeket gyakoroltató feladatsor, amellyel már az előző leckében találkozhattunk. Az ott szereplő megoldásokat kell újraalkotni a függvények ismeretében: azaz a megoldó program egyes funkcióit (beolvasást, a tételt megvalósító lényegi részt és az eredménymegjelenítést) kell egy-egy függvénybe beleágyazni.
  • Fájlméret: 0.07 MB

{esszé}

Fogalmazza meg, hogy a mi a különbség a függvény formális és aktuális paraméterei között! Adjon példát egy függvény fejsorára, és ugyanennek a függvénynek a hívására! A híváshoz ne felejtse el leírni a megfelelő paraméterek deklarációját!

{/esszé}

Függvények használata; tömbök függvényekben

Tudnivalók a függvényekről

A függvények lényege

A függvény programozásbeli fogalmának lényege a következő: egy olyan viszonylagosan zárt, önálló programegység, amely a bemenetéül szolgáló adatok felhasználásával előállítja a kimenetéül meghatározott halmazba tartozó értéket.

A bemenetéül szolgáló adatokat általában az argumentumában soroljuk föl, amelyek neve, sorrendje és típusa kötött. A kimenetét alkotó értékek halmazának a függvény ún. értéktípusa felel meg.

A „viszonylagos zártsága” arra utal, hogy általában csak a paraméterein keresztül tartja a kapcsolatot a környezetével. Bizonyos kényelmi okok miatt lesz példa arra, hogy egy függvény a program paramétereit közvetlenül, azaz nem a paraméterezés útján éri el.

Az „önálló” jelző pedig azt fejezi ki, hogy jól meghatározható az a (rész)feladat, amelyet meg kell oldania, így minden olyan programba változatlanul beilleszthető, amelyben ez a részfeladat fölvetődik. Így válik hasznos eszközévé a felülről lefelé történő programtervezésnek.

A függvények „alakjai”

A függvények a programozás során (is) kétféle szituációban bukkan(hat)nak föl: amikor fölhasználjuk, és amikor definiáljuk azokat. Amikor fölhasználjuk, akkor a paraméterei azok az objektumok, amelyekkel dolgoznia kell. Ezeket a paramétereket aktuális paramétereknek nevezik. Amikor definiáljuk a függvényszámítást, akkor a paraméterek azt írják le, hogy milyen módon játszanak szerepet a számításban. Ezek az ún. formális paraméterek.

Specifikációban

A specifikációban a függvényekre hivatkozás nem különbözik a matematikában megszokottól. A definiáláshoz a specifikációban egy külön rész szolgál, amelyben először az adott függvény értelmezési tartományát és értékkészletét adjuk meg (ez az ún. szignatúra), majd a leképezés szabályát. Egy korábbi gyakorlatból idézzük az alábbi specifikációrészletet:

Utófeltétel

PrímN∈[10N-1+1 .. 10N-1] és (PrímN Mod 10)=1 és Prím?(PrímN) és …

Definíció

Prim?:Egész→Logikai

Prím?(x):= ∀i∈[2..Gyök(x)]: nem (i | x)

Algoritmusban

Algoritmizálás közben a hivatkozás a „szokásnak” megfelelően történik, a definiáláshoz viszont egy új struktogramot kell szentelni, amelyben két újdonság van:

1) a fejsor – a függvény nevén felül – felsorolja az értelmezési tartományt megtestesítő paraméterek „lokális” neveit, azaz amikkel a függvény törzsén belül lehet rájuk hivatkozni;

2) a függvény értékét egy, a függvény nevével megegyező nevű változó képviseli, a neki adott érték lesz a függvény végső (visszaadott) értéke.

Példa

A függvénydefiniálásra egy struktogram-példa:

A Prím-e függvény algoritmusa, struktogrammal.

C++ kódban

A C++ kódban a hivatkozás az eddigiekhez hasonlóan egyszerű: függvényNév(paraméterek). A definiáláshoz alapelv, hogy a fordítóprogramnak már az előtt ismernie kell a függvényhívás szintaktikáját, azaz a függvény szignatúráját, mielőtt használnánk, hivatkoznánk rá. Természetesen a fordításhoz a függvény működését leíró törzs is kell, de azt szabad „halogatni”, a forrásprogram végére hagyni. Így a javasolt programszerkezet az alábbi:

#include <iostream>
using namespace std;
… fv-szignatúrák …
int main()
{
    … main-törzs …
    return 0;
}
… fv-definiciók …

A fv-szignatúra szintaxisa a deklarációkéra emlékeztet; újdonság a paraméterezés jelölése:

típ fv-azonosító(formális paraméterezés);

Jegyezzük azonban meg, hogy a paraméterezés még akkor sem hagyható el, ha valójában nincs paramétere, azaz a zárójelek nem hagyhatók el!

A fv-definíció szintaxisa – nem meglepő módon – nagyban emlékeztet a már megismert main() függvény szintaxisára:

típ fv-azonosító(formális paraméterezés)
{
    … a fv-törzs …   //megegyezik a main-törzs szintaxisával
    return fv-érték; //fv-érték: egy kifejezés, amely a visszaadandó 
                     //értéket képviseli; típusa: típ
}
Példa

Mindennél többet mond egy konkrét kód-példa:

#include <iostream>
using namespace std;
bool PrimE(int x);
int main()
{
    …
    if (PrimE(N))
    {
      …
    }
    …
    return 0;
}
bool PrimE(int x)
{
    int i=2;
    while (i*i<=x  &&  i%x!=0)
    {
       ++i;
    }
    return i*i>x;
}
Megjegyzés

Megjegyzések a konkrét kódhoz:

  • Nem okozhat meglepetést, hogy a Prím? függvényt némileg át kellett keresztelnünk: a hosszú „í” és a „?” nem lehet azonosító része (a C++ nyelvben). Így logikusan jön az elnevezés: PrimE.
  • A kódolásnál a számítás gyorsítása kedvéért egy azonos átalakítással éltünk: i≤Gyök(x) ↔ i*i≤x. Így megspóroltunk egy hosszas számítást igénylő függvény használatát. (Ez az sqrtf függvény, amely miatt még a cmath header fájlt is be kell szúrnunk (inkludálnunk⇒#include) kellett volna.)

A függvényeknek van egy olyan fajtája, amely nem úgy ad vissza értéket, mint egy függvény, hanem paraméterei közt van olyan, amely értékét módosítja. Ezeket eljárásfüggvényeknek (vagy egyszerűen: eljárásoknak) nevezik. Ekkor a típusaként a void („üres, mentes” jelentésű) szót kell megadni. A return utasításra ekkor nincs szükség.

Paraméterátadás

Kétféle számunkra érdekes paraméterátadási módszer van: az érték szerinti, illetve a hivatkozás szerinti.

Az érték szerinti paraméterátadás lényege: a formális paraméterből keletkezett lokális változóba másolódik a híváskor az aktuális paraméter értéke, így ennek a törzsön belüli megváltozása nincs hatással az aktuális paraméterre.

A hivatkozás szerinti paraméterátadás lényege: a formális paraméterbe az aktuális paraméter címe (rá való hivatkozás) kerül, a lokális néven is elérhetővé válik. Így értelemszerűen akár meg változtatni is képes azt.

C++ kódolás

A függvény szignatúrájában jelölni kell, hogy a paraméterátadás melyik fajtáját írjuk elő. Hogy ezt miként kell tükrözni a C++ nyelvben, írjuk le alább.

Skalárváltozók esetén az érték szerinti: „típus formális-paraméter”; a hivatkozás szerinti: „típus &formális-paraméter”.

Példa
int max(int x, int y)

vagy ugyanez eljárásfüggvényként:

void max(int x, int y, int &maxxy)

Tömbváltozók esetén az érték szerinti: „const típus formális-paraméter[]”; a hivatkozás szerinti: „típus formális-paraméter[]”. Vegyük észre a C++ filozófiaváltását: tömböknél a hivatkozás szerintit tekinti alapvetőbbnek (⇒egyszerűbben leírhatónak).

void ki_int_tomb(const int x[], int n)
void be_int_tomb(int x[], int &n, int maxN)

Az animáció bemutatja a függvények használatát.

Az animáció bemutatja a függvények használatát.

Flash lejátszó letöltése

Függvények használata

Vissza a tartalomjegyzékhez

Feladatok

1. feladat

Feladat

Készítsük el azt az eljárást, amely beolvas egy min..max közötti int számot! Ha a max<min, az jelentse azt, hogy max=+∞! Az eljárás szignatúrája az alábbi:

void be_int(string kerdes, int &n, int min, int max, string uzenet)

Megoldás

Specifikáció

Bemenet

kérdés,üzenet:Szöveg, min,max:Egész

Kimenet

n:Egész

Előfeltétel

Utófeltétel

∃i>0: n=cini és (min≤max → n∈[min..max]) és

(min>max → n≥min) és

∀j∈[1..i): coutj=üzenet

Magyarázat az utófeltételhez: A „∃i: n=cini” azt fejezi ki, hogy előbb-utóbb (pontosabban az i. próbálkozásra) a billentyűzetre kerül számként értelmezhető jelsorozat, amely teljesíti a határok által megfogalmazott feltételeket. Az addigi elvárást fejezi ki a „∀j∈[1..i): coutj=üzenet” feltételrész, amit így olvashatunk: „az első helyes inputig (az i. próbálkozást megelőzően) a konzol outputra a megadott (hiba-)üzenet kerül”.

Algoritmus

Az 1. feladat algoritmusa, struktogrammal.

Kód

//beolvassa a min..max közötti egész számot (max<min => max=végtelen)
void be_int(string kerdes, int &n, int min, int max, string uzenet)
{
    bool hiba;
    string tmp;
    do
    {
      if (max>=min)
      {
        cout << kerdes << " (" << min << ".." << max << "):"; cin >> n;
        hiba=cin.fail() || n<min || n>max;
      }
        else
      {
        cout << kerdes << " (" << min << "..):"; cin >> n;
        hiba=cin.fail() || n<min;
      }
      if (hiba)
      {
        cout << uzenet << endl;
        cin.clear(); getline(cin,tmp,'\n');
      }
    }while(hiba);
}

2. feladat

Feladat

Készítsük el azt az eljárást, amely megcserél két int értéket! Az eljárás szignatúrája:

void csere_int(int &a, int &b)

Megoldás

Specifikáció

Bemenet

a,b:Egész

Kimenet

a,b:Egész

Előfeltétel

Utófeltétel

a’=b és b’=a

Algoritmus

A 2. feladat algoritmusa, struktogrammal.

Éltünk a korábban bevezetett struktogram egyszerűsítési lehetőséggel, amely során több értékadást is egyetlen dobozba sűrítettünk.

Kód

//csere: a<=>b
void csere_int(int &a, int &b)
{
    int c=a; a=b; b=c;
}

3. feladat

Feladat

Építsük össze az előbbi két eljárást egy „próbaprogramba” amely csak pozitív számokat fogad el!

Megoldás

Specifikáció

Bemenet

A,B:Egész

Kimenet

A,B:Egész

Előfeltétel

Utófeltétel

A’=B és B’=A

Algoritmus

Banálisan egyszerű: a nem algoritmizálandó beolvasáson és kiíráson túl a csereeljárás meghívását tartalmazza, valamint a felhasznált két eljárás struktogramját. Ezért nem részletezzük.

Kód

#include <iostream>
using namespace std;
//beolvassa a min..max közötti egész számot (max<min => max=végtelen)
void be_int(string kerdes, int &n, int min, int max, string uzenet);
//csere: a<=>b
void csere_int(int &a, int &b);
int main()
{
    int A,B;
    be_int("Kérem az 'A'-t (pozitív egész):",A,1,0,"Nem jó szám!");
    be_int("Kérem a 'B'-t (pozitív egész):",B,1,0,"Nem jó szám!");
    csere_int(A,B);
    cout << "Az 'A':" << A << endl;
    cout << "A 'B':" << B << endl;
    return 0;
}
//beolvassa a min..max közötti egész számot (max<min => max=végtelen)
void be_int(string kerdes, int &n, int min, int max, string uzenet)
{
    bool hiba;
    string tmp;
    do
    {
      if (max>=min)
      {
        cout << kerdes << " (" << min << ".." << max << "):"; cin >> n;
        hiba=cin.fail() || n<min || n>max;
      }
        else
      {
        cout << kerdes << " (" << min << "..):"; cin >> n;
        hiba=cin.fail() || n<min;
      }
      if (hiba)
      {
        cout << uzenet << endl;
        cin.clear(); getline(cin,tmp,'\n');
      }
    }while(hiba);
}
//csere: a<=>b
void csere_int(int &a, int &b)
{
    int c=a; a=b; b=c;
}

4. feladat

Egy korábbi leckében a következő feladat került szóba, amelyet ismét előveszünk, de az „újdonságok” kedvéért:

Feladat

Határozzuk meg az első 1-re végződő N jegyű prímszámot! Oldjuk meg most függvények felhasználásával!

Megoldás

Specifikáció

Ezt a specifikációt készítettük akkor:

Bemenet

N:Egész

Kimenet

PrímN:Egész

Előfeltétel

N>1 [ilyen garantáltan létezik]

Utófeltétel

PrímN∈[10N-1+1 .. 10N-1] és (PrímN Mod 10)=1 és Prím?(PrímN) és
∀pm∈[10N-1+1 .. 10N-1]: (pm Mod 10)=1 és Prím?(pm) → pm≥PrímN

Definíció

Prim?:Egész→Logikai

Prím?(x):= ∀i∈[2..Gyök(x)]: nem (i | x)

Algoritmus

Az akkori algoritmusok alapján az alábbiakat alkothatjuk meg. Csak a legfelsőbb szintű, lényegi algoritmusban (PrímKeresés-ben) van elenyésző változás. A változást épp a függvénnyé alakítás okozza.

A kiinduló algoritmus

A függvényesített algoritmus

A 4. feladatbeli kiinduló algoritmus, struktogramja.

A 4. feladatbeli függvényesített algoritmus, struktogramja.

Változatlanul hagyhatjuk a Prím?() függvényt:

A Prím-e függvény algoritmusa, struktogrammal.

Kód

A kódoláskor felhasználtuk a korábban elkészített egész-beolvasó eljárást és a PrimE() függvényt. Az algoritmus alapján kódoltuk a PrimKereses() függvényt, és egy később is felhasználható, billentyűlenyomásra várakoztató eljárást is készítettünk.

#include <iostream>
#include <windows.h>
using namespace std;
//beolvassa a min..max közötti egész számot (max<min => max=végtelen)
void be_int(string kerdes, int &n, int min, int max, string uzenet);
//n-jegyű prímet keres:
int PrimKereses(int n);
//eldönti: x prímszám-e
int PrimE(int x);
//billentyű-lenyomásra várakozik
void billreVar();
int main()
{
    int N;    //a jegyek száma
    int PrimN;//N jegyű prím
    be_int("A jegyek száma (>1):",N,2,0,"Nem jó számhossz!");
    PrimN=PrimKereses(N);
    cout << "Az első 1-re végződő " << N << "-jegyű prím:" 
<< PrimN << endl; billreVar(); return 0; } //beolvassa a min..max közötti egész számot (max<min => max=végtelen) void be_int(string kerdes, int &n, int min, int max, string uzenet) { bool hiba; string tmp; do { if (max>=min) { cout << kerdes << " (" << min << ".." << max << "):"; cin >> n; hiba=cin.fail() || n<min || n>max; } else { cout << kerdes << " (" << min << "..):"; cin >> n; hiba=cin.fail() || n<min; } if (hiba) { cout << uzenet << endl; cin.clear(); getline(cin,tmp,'\n'); } } while(hiba); } //n-jegyű prímet keres: int PrimKereses(int n) { //10^(n-1) (n>1): int tn=1; for (int i=1;i<n;++i) { tn*=10; } int i=tn+1; while (!PrimE(i)) { i+=10; } return i; } //eldönti: x prímszám-e int PrimE(int x) { int i=2; while (i*i<=x && i%x!=0) { ++i; } return i*i>x; } //billentyű-lenyomásra várakozik void billreVar() { system("pause");//windows esetében! }

1. házi feladat

Feladat

Írjuk meg azt az eljáráspárt, amely beolvas, illetve kiír egy N elemű tömböt! A tömb legyen statikusan deklarálva. Szignatúráik legyenek az alábbiak:

void be_int_tomb(int x[], int &n, int maxN)
void ki_int_tomb(const int x[], int n)

Majd ezek kipróbálására a következő feladatú eljárást is készítsük el: a beolvasott elemek sorrendjének a megfordítsa! Használjuk föl a korábbi csere(…) eljárást is! A megfordítást végezze az alábbi alprogram (a paraméterezés „megálmodása” is a feladat része):

void megfordit_int_tomb(  ???  , ???  )

2. házi feladat

Feladat

Adja meg egy természetes szám prímtényezős felbontását! A programban használjuk az előbbiekben létrehozott eljárásokat, valamint egy újat, amely egy tömbben sorolja föl a prímtényezőket!

3. házi feladat

Feladat

Válasszon ki az eddigi gyakorlatokon elkészített programok közül néhányat, és írja át ezeket a fentiek szellemében úgy, hogy a beolvasást, a lényegi tevékenységet és a kiírást függvényekkel valósítja meg!

Vissza a tartalomjegyzékhez

Fel a lap tetejére
Új Széchenyi terv
A projekt az Európai Unió támogatásával, az Európai Szociális Alap társfinanszirozásával valósul meg.