5. gyakorlat: függvények, top-down tervezés
A mai témák: felülről lefelé tervezés (top-down tervezés) gyakorlása, függvények írása és a karakter típus használata.
1 nagybetu(), kisbetu()
Írjunk függvényeket, amelyek karaktereket kapnak paraméterként, és a visszatérési értékük a nagybetűsített/kisbetűsített karakter: pl. a→A. Egyéb bemenetek esetén pedig adják változatlanul vissza az eredetit, pl. 8→8.
Megoldás
A betűk is csak számok a gép számára. Így aztán két karakter kivonása is értelmes művelet.
Az általában használt ASCII kódtáblában egymás mellett vannak ábécé sorrendben a nagybetűk,
és egy másik tartományban egymás mellett a kisbetűk. Emiatt a 'C'-'A' kifejezés
2-t ad (mivel a C és az A betű között 2 a távolság). Az 'A'-'a' kifejezés
a két tartomány távolságát adja meg.
/* visszaadja a karakter nagybetus parjat, vagy sajat magat */
char nagybetu(char c)
{
if (c>='a' && c<='z')
return c-'a'+'A';
return c;
}
/* visszaadja a karakter kisbetus parjat, vagy sajat magat */
char kisbetu(char c)
{
if (c>='A' && c<='Z')
return c-'A'+'a';
else
return c;
}
2 Madárnyelv
Adott az alábbi program, amely madárnyelven (mavadávárnyevelveven) írja ki a beírt szöveget. Ezt mindenki megoldotta laboron.
#include <stdio.h>
int main()
{
char c;
while (scanf("%c", &c)==1) {
if (c=='a' || c=='e' || c=='i' || c=='o' || c=='u')
printf("%cv%c", c, c);
else
printf("%c", c);
}
return 0;
}
Írjunk függvényt, amelyik megmondja egy betűről, hogy magánhangzó-e! Alakítsuk át úgy
a programot, hogy a megírt függvényt használjuk a main()-ben!
Megoldás
#include <stdio.h>
/* Igaz ertekkel ter vissza, ha a parametere egy maganhangzo. */
int maganhangzo(char c)
{
return c=='a' || c=='e' || c=='i' || c=='o' || c=='u';
}
int main()
{
char c;
while (scanf("%c", &c)==1)
if (maganhangzo(c))
printf("%cv%c", c, c);
else
printf("%c", c);
return 0;
}
Hogyan lehetne megoldani azt, hogy a nagybetűvel kezdődő szavakat is
helyesen kezelje a program? Pl. az „Alma” szóra azt kell kiírnia, hogy „Avalmava”.
Ehhez fel kell tudnia ismerni a nagybetűvel írt magánhangzókat is. Kiíráskor
a v betű után viszont már kisbetűvel kell kiírnia.
Megoldás
Egyszerű! A fenti, magánhangzó vizsgálatát végző függvénynek ne a beolvasott karaktert
adjuk, hanem annak kisbetűsített párját. Így az nem is fogja látni, hogy nagybetűről
vagy kisbetűről van szó. Innen adódik lent a maganhangzo(kisbetu(c)) kifejezés.
Másrészt, a kiírásnál a v betű utáni duplázott magánhangzót ne az eredeti
formájában írjuk ki, hanem kisbetűsítve. Ebből jön a printf() paramétereiben
hívott kisbetu() függvény:
while (scanf("%c", &c)==1)
if (maganhangzo(kisbetu(c)))
printf("%cv%c", c, kisbetu(c));
else
printf("%c", c);
3 Kiírás adott számrendszerben
Írjunk programot, amelyik adott számot ír ki adott számrendszerben!
Megoldás
Mivel a kiírás balról jobbra halad, ezért először a legnagyobb helyiértékű számjegyre van szükségünk. Például ha 10-es számrendszerben szeretnénk kiírni a 123-at, akkor először az 1-est, utána a 2-est, végül pedig a 3-ast kell.
Ezért kell egy ciklus, amely a legnagyobb helyiértéktől a legkisebbig
halad. A 123 példájában n=2-től (százasok) n=0-ig (egyesek). A megoldásban
ez szerepel a kiir() függvényben. Hogy ez működni tudjon,
két másik alproblémára kell még megoldás:
- Tudnunk kell, melyik a legnagyobb helyiérték. Ehhez majd megírjuk a
szamjegyek_szama()függvényt. - Ki kell tudnunk vágni a számból egy adott helyiértékű számjegyet.
Ehhez pedig az
n_edik_szamjegy()függvény lesz használható.
Ehhez az egyszerű feladathoz természetesen nem lenne feltétlenül szükséges függvényeket használni. De jól mutatják azt, hogyan lehet kisebb részekre bontani a problémát.
#include <stdio.h>
/* megmondja, hogy egy adott szam egy adott szamrendszerben
* hany szamjegybol all. */
int szamjegyek_szama(int szam, int szamrendszer)
{
int szamjegyek=0;
while (szam>0) {
szam/=szamrendszer;
szamjegyek+=1;
}
return szamjegyek;
}
/* adott szam adott szamrendszerbeli alakjanak n. szamjegyet
* adja vissza. n a kitevonek megfelelo hatvanykitevo,
* vagyis n=0 adja az egyeseket, n=1 a tizeseket stb.,
* ha epp 10-es szamrendszerben vagyunk. */
int n_edik_szamjegy(int szam, int szamrendszer, int n)
{
/* pelda: szam=1234, n=2 -> szam=12 lesz, mert 2x osztja */
while (n>0) {
szam/=szamrendszer;
n-=1;
}
/* pelda: szam=12 itt, szam%10 -> 2-vel ter vissza */
return szam%szamrendszer;
}
/* adott szám kiírása adott számrendszerben. */
/* először ki kell találni, az egész hány számjegyből áll;
mivel a legnagyobb helyiértékűt kell kiírni először. */
void kiir(int mit, int miben)
{
int n;
/* a kiírandó számjegyek, visszafelé */
for (n=szamjegyek_szama(mit, miben)-1; n>=0; n-=1)
printf("%d", n_edik_szamjegy(mit, miben, n));
}
int main()
{
kiir(123, 10); printf("\n");
kiir(255, 2); printf("\n");
return 0;
}
A programban a megfelelő helyen nem véletlenül szerepel while és for
ciklus. Az elsőnél a while praktikusabb (az igazi „amíg” jellegű),
a másodiknál inkább a for (számlálásos jellegű).
Módosítsuk úgy a programot, hogy 10-nél nagyobb alapú számrendszerben is (pl. 16-osban) működjön! A 10-et, és annál nagyobb számjegyeket ilyenkor betűkkel szokás jelölni. Pl. 16-osban a 0…15 számjegyek: 012…89ABCDEF.
Megoldás
Ehhez csak a kiírást kell módosítani, hiszen az n_edik_szamjegy()
függvény már eddigi formájában is elő tudja állítani a 10-nél nagyobb számjegyeket
is. A kiírandó karaktert így tudjuk előállítani:
/* Visszatér egy karakterrel, amely az adott számjegyet ábrázolja.
* 0..9 számjegyek -> '0'..'9' karakterek.
* 10-nél nagyobb számjegyek -> betűk 'A'-tól kezdődően. */
char szamjegy_karakter(int szj)
{
if (szj<10)
return szj+'0';
else
return szj-10+'A';
}
4 Minimum, maximum, határ
Írjunk olyan függvényt, amely:
- Visszaadja két egész szám közül a nagyobbikat:
max(a, b). - Visszaadja két egész szám közül a kisebbiket:
min(a, b).
Írjunk olyan függvényt is a fentiek használatával, amely:
- Alulról korlátoz egy értéket:
alulrol(szam, min)aminparaméter értékét adja vissza, ha a megadott szám alatta van; amúgy pedig magát a számot. - Felülről korlátoz egy értéket:
felulrol(szam, max)ugyanígy. - Két oldalról korlátoz egy értéket:
korlatoz(szam, min, max)adja vissza a számot, haminésmaxközé esik, amúgy pedigmin-t vagymax-ot attól függően, hogy merre haladta meg a tartományt.
Megoldás
#include <stdio.h>
/* Visszaadja a két egész szám közül a kisebbiket. */
int min(int a, int b)
{
if (a<b) /* ha "a" kisebb */
return a;
else /* amugy "b" kisebb, vagy egyenloek */
return b;
}
/* Visszaadja a két egész szám közül a nagyobbikat. */
int max(int a, int b)
{
if (a>b)
return a;
else
return b;
}
/* Visszaadja a számot, ha az nagyobb, mint min, amúgy pedig min-t. */
int alulrol(int szam, int min)
{
return max(szam, min);
}
/* Visszaadja a számot, ha az kisebb, mint max; amúgy a
* maxot. Ugyanaz a helyzet, mint az előbbinél. */
int felulrol(int szam, int max)
{
return min(szam, max);
}
/* A [min;max] intervallumba szorítja a megadott számot, és
* azzal tér vissza. Csak akkor működik helyesen, ha min<=max. */
int korlatoz(int szam, int min, int max)
{
return felulrol(alulrol(szam, min), max);
}
int main()
{
int i;
printf("min(5, 7)=%d\n", min(5, 7));
printf("max(5, 7)=%d\n", max(5, 7));
printf("alulrol(5, 0)=%d\n", alulrol(5, 0));
printf("alulrol(-1, 0)=%d\n", alulrol(-1, 0));
printf("felulrol(5, 0)=%d\n", felulrol(5, 0));
printf("felulrol(-1, 0)=%d\n", felulrol(-1, 0));
printf("-5..10 számok [0;5] közé korlátozva:\n");
for (i=-5; i<=10; i+=1)
printf("%d ", korlatoz(i, 0, 5));
printf("\n");
return 0;
}
Beugrató: az alulrol() függvény a max() hívásával oldható meg. Ugyanis
ha a limit alá csúszik a szám, akkor a limit lesz a nagyobb; Ha a limit felett van, akkor pedig a szám!
Ugyanígy, a felulrol() függvényben a min()-t kell használni.
Természetesen megoldhatóak lennének az utóbbiak a min() és max()
használata nélkül is. A korlatoz() pedig megoldható lenne a min()
és max() használatával – ebben az esetben azonban figyelni kellene arra, hogy
a formális paramétereit nem szabadna min-nek és max-nak elnevezni,
hiszen akkor nem lehetne belőle meghívni a min() és max()
függvényeket:
int korlatoz(int szam, int min, int max) // HIBÁS!
{
return min(max(szam, min), max); // HIBÁS!
}
A formális paraméter neve elfedi a függvényen kívül megadott
másik függvény nevét. Érdemes ezt átgondolni a fent helyesen megírt
alulrol() és felulrol() esetén is! Ott is
megtörténik ez, csak nem probléma, mert mindkettőnél pont a másik
függvényre van szükség.
5 Beolvasás adott számrendszerben
A szám kiírása feladat fordítottja: olvassunk be a billentyűzetről
egy számot a megadott számrendszerben. (Előbb láttuk, mennyi dolga van ezzel
a printf()-nek, most látjuk, mennyi dolga van a scanf()-nek.)
Kövessük itt is a felülről lefelé tervezés elvét!
Megoldás
#include <stdio.h>
#include <ctype.h>
/* Visszaadja a számjegy értékét:
* - 0-9 -> 0-9
* - A-Z (a-z) -> 10-35
* - egyéb karakter (érvénytelen) -> -1 */
int ertek(char c)
{
if (isdigit(c))
return c-'0';
if (isalpha(c))
return toupper(c)-'A'+10;
/* nem szám, nem betű - ez bizony hiba */
return -1;
}
/* beolvas egy egész számot a billentyűzetről,
* az adott számrendszerben. visszatérési értéke
* a beolvasott szám, vagy hiba esetén -1. */
int beolvas(int alap)
{
int szam;
char c;
szam=0;
while (scanf("%c", &c)==1 && !isspace(c)) {
int ert = ertek(c);
if (ert<0 || ert>=alap) /* ha hiba, vagy túl nagy az alaphoz */
return -1;
szam = szam*alap + ert;
}
return szam;
}
int main()
{
printf("Irj 2-esben: ");
printf("Erteke 10-esben: %d\n", beolvas(2));
printf("Irj 16-osban: ");
printf("Erteke 10-esben: %d\n", beolvas(16));
return 0;
}
6 Tökéletes és barátságos számok
Tökéletes szám az a szám, amely megegyezik a nála kisebb osztóinak összegével. A legkisebb tökéletes szám a 6 (1+2+3), az utána következők a 28 (1+2+4+7+14) és 496.
Barátságos számoknak nevezzük azokat a számpárokat, amelyeknél az egyik szám nála kisebb osztóinak összege egyenlő a másik számmal, és fordítva. A legkisebb ilyen számpár a (220;284), mert 1+2+4+5+10+11+20+22+44+55+110=284 és 1+2+4+71+142=220. További párok (1184;1210) és (2620;2924).
Írjunk függvényt, amely 1) megmondja egy számról, hogy tökéletes-e, 2) megmondja egy számpárról, hogy barátságos-e! Kövessük a top-down tervezési elvet!
Megoldás
Induljunk ki a definícióból: tökéletes szám az, amelyik megegyezik a nála kisebb osztóinak összegével. A kisebb osztókat összeadő függvényt majd megcsináljuk később.
/* igazzal tér vissza, ha tökéletes szám */
int tokeletes(int szam)
{
return szam==kisebboszto_osszeg(szam);
}
Barátságos számpár pedig az, amelyeknél egymásra vetítve működik ez ugyanígy.
/* igazzal tér vissza, ha barátságosak */
int baratsagos(int szam1, int szam2)
{
return szam1==kisebboszto_osszeg(szam2) && szam2==kisebboszto_osszeg(szam1);
}
A tökéletes szám és a barátságos számok definíciója nagyon hasonlít egymáshoz. Tulajdonképpen a tökéletes szám önmaga barátja. Vegyük észre: az osztós függvény létezését feltételezve egy-egy sorban meg tudtuk oldani a problémákat! Sőt, mivel mindkét függvény ugyanannak a segédfüggvénynek a létezését feltételezi, elég azt már egyszer megírnunk! A teljes program:
#include <stdio.h>
/* visszatér a paraméterként kapott szám
osztóinak összegével (magát a számot kivéve) */
int kisebboszto_osszeg(int szam)
{
int osszeg, oszto;
osszeg=0;
for (oszto=1; oszto<=szam/2; oszto+=1)
if (szam%oszto == 0)
osszeg += oszto;
return osszeg;
}
/* igazzal tér vissza, ha tökéletes szám */
int tokeletes(int szam)
{
return szam==kisebboszto_osszeg(szam);
}
/* igazzal tér vissza, ha barátságosak */
int baratsagos(int szam1, int szam2)
{
return szam1==kisebboszto_osszeg(szam2)
&& szam2==kisebboszto_osszeg(szam1);
}
int main()
{
printf("6 tökéletes: %d\n", tokeletes(6));
printf("7 tökéletes: %d\n", tokeletes(7));
printf("220,284 barátságos: %d\n", baratsagos(220, 284));
printf("220,285 barátságos: %d\n", baratsagos(220, 285));
return 0;
}
