• Nem Talált Eredményt

Feltételes kifejezések

Az if...then...else kifejezésre már korábban is láttunk példát, most azonban azt is ki szeretnénk hangsúlyozni, hogy az if a Haskellben nem utasítás vagy állítás, hanem egy feltételes kifejezés, ezért az else ág kötelező.

4.9. feladat Írjunk egy Haskell-függvényt, amely megvizsgálja, hogy x osztható-e y-nal.

oszthato :: (Integral a) => a -> a -> Bool

oszthato x y = if mod x y == 0 then True else False

4.6. Feltételes kifejezések 51

> oszthato 21 7 True

A korábban megírt faktoriális függvényt is lehet feltételes kifejezésekkel definiálni:

fakt :: (Eq a, Num a) => a -> a

fakt n = if n == 0 then 1 else n * fakt (n-1)

> fakt 100

9332621544394415268169923885626670049071596826438...

Többágú kifejezések megadására a case...ofkifejezés alkalmazható, amelynek használatát egy feladaton keresztül mutatjuk be, ahol szükségünk lesz a chrfüggvényre is. Ahhoz, hogy tudjuk ezt használni, importálni kell aData.Charkönyvtárcsomagot. Achrfüggvény meghatározza azt az Uni-code kódolás szerinti szimbólumot, amely a paraméterként megadott egész számhoz tartozik.

4.10. feladat Írjuk meg a hexaSz Haskell-függvényt, amely meghatározza azt a 16-os számrendszerben használt szimbólumot, amelyet egy 0 és 15 közötti számjegyhez rendelnek hozzá. A kiíratást az A, B, C, D, E, F szimbólumok segítségével végezzük.

import Data.Char hexaSz :: Int -> Char hexaSz c

| c >= 0 && c < 16 = case c of

10 -> 'A' 11 -> 'B' 12 -> 'C' 13 -> 'D' 14 -> 'E' 15 -> 'F'

_ -> chr (c + 48)

| otherwise = error "hibas bemenet"

> hexaSz 15 'F'

> hexaSz 19

*** Exception: hibas bemenet...

A fenti kódsorban acasehét esetet tárgyal aszerint, hogy acértéke mennyi.

Az utolsó eset, azaz acaseutolsó sora azt az esetet kezeli, amikor a koráb-bi feltételek közül egyik sem teljesült. A _ szimbólum mindenes operátor, szerepe az, hogy olyan helyzeteket kezeljen, amikor nem számít a bemenet vagy a mintázott érték. Használatával a fenti függvényben a minden más esetben adjuk meg a függvény kimeneti értékét, azaz ha a c nem egyen-lő 10, 11, 12, 13, 14, 15-tel, akkor a chr (c + 48) kifejezés kerül kiértékelésre. Mivel a '0' karakter kódja 48, a chr (c + 48) kifejezés meghatározza a cszámjegy karakter-értékét.

4.11. feladat Írjunk egy Haskell-függvényt, amely meghatározza egy16-os számrendszerben megadott számsorozatnak azon alakját, amelyben a szám-értékeket a16-os számrendszerben használt szimbólumokkal helyettesítjük.

Használjuk a korábban megírt hexaSzfüggvényt.

hexaLs :: [Int] -> [Char]

hexaLs [] = ""

hexaLs (k : ve) = hexaSz k : hexaLs ve

> hexaLs [12, 4, 5, 15, 7, 0, 11, 4]

"C45F70B4"

> hexaLs [12, 4, 5, 17, 7, 0, 11, 4]

*** Exception: hibas bemenet...

A függvénytörzs mintaillesztés alapján választja szét a triviális esetet az ál-talánostól, azaz amikor a bemenet üres lista, illetve amikor nem. Ez utóbbi esetben a : operátor segítségével választottuk le a lista első elemét, a k-t a lista többi részétől, a ve-től. Az egyenlőség jobb oldalán a k, illetve ve étékekkel így műveleteket tudunk végezni. Ez jelen esetben azt jelenti, hogy a bemeneti egész számokat tartalmazó lista alapján felépítünk egy új lis-tát. Az új listát úgy építjük fel, hogy alkalmazzuk a hexaSz függvényt a bemeneti lista első elemén, ak-n, majd ezt az értéket a :operátor segítsé-gével a rekurzív hívás során nyert lista elé fűzzük. Az újonnan épített lista lesz a függvény kimenetének értéke. Figyeljük meg a:operátor szerepét az egyenlőség bal, illetve jobb oldalán, a későbbiekben még visszatérünk rá.

4.7. Halmazkifejezések 53

4.7. Halmazkifejezések

Halmazkifejezéseket vagy listagenerátorokat iteratív adatszerkezetek (listák, halmazok, sorozatok) elemeinek megadásakor alkalmazunk. Az an-gol terminológia erre a list comprehension megnevezést használja. Ezzel a jelölésrendszerrel nagyon egyszerűen lehet listaelemeket megadni, generál-ni, feldolgozni. A legegyszerűbb feladatok egy lista elemeiből kiindulva azon listaelemekből hoznak létre új listát, amelyek eleget tesznek egy adott fel-tételnek.

4.12. feladat Írjunk egy Haskell-függvényt, amely meghatározza a paramé-terként megadott szám osztóit.

osztok :: Integral a => a -> [a]

osztok n = [i | i <- [1..n], mod n i == 0]

> osztok 60

[1,2,3,4,5,6,10,12,15,20,30,60]

A kódsorban azokból az i értékekből hozzuk létre az eredménylistát, ame-lyek az[1..n]listának elemei, és eleget tesznek a mod n i == 0 feltétel-nek.Halmazkifejezések megadásakor egy adott lista elemei alapján hozunk létre egy új listát, ahol az új listaelemekre vonatkozó szabályokat szögletes zárójelben kell megadni. A szögletes zárójelbe írtak két részre oszthatók, a | jel előtti részre, illetve az utána következő részre. Az első részben az új lista elemeit vagy a listaelemekre vonatkozó kifejezéseket adhatunk meg.

A második részben a <- szimbólum segítségével a listaelemek generálási módját határozzuk meg. A,utáni rész nem kötelező, ide logikai kifejezések, megszorítások írhatók.

4.13. feladat Írjunk egy Haskell-függvényt, amely meghatározza egy16-os számrendszerben megadott számsorozatnak azon alakját, ahol a számér-tékeket a 16-os számrendszerben használt szimbólumokkal helyettesítjük.

Használjuk a korábban megírt hexaSzfüggvényt.

hexaLc :: [Int] -> [Char]

hexaLc ls = [hexaSz k | k <- ls]

> hexaLc [12, 4, 5, 15, 7, 0, 11, 4]

"C45F70B4"

A feladatot korábban is megoldottuk, most halmazkifejezéssel azonban egy egyszerűbb kódsort tudtunk írni. Fontos ugyanakkor leszögezni, hogy haté-konyság szempontjából ez a módszer nem lesz jobb.

4.14. feladat Írjunk egy Haskell-függvényt, amely egy olyan háromelemű tuple-ökből álló listát generál, ahol a tuple-elemek a 0, 1,...,n összes lehetséges értékeit felveszik, az összes lehetséges módon.

tupleLc :: (Num a, Enum a) => a -> [(a, a, a)]

tupleLc n = [(x, y, z) | x <- [0..n], y <- [0..n], z <- [0..n]]

> tupleLc 1

[(0,0,0),(0,0,1),(0,1,0),(0,1,1),(1,0,0),(1,0,1), (1,1,0),(1,1,1)]

4.15. feladat Írjunk egy Haskell-függvényt, amely meghatározza adott n-ig a pitagoraszi számhármasokat, ahol az x, y, z három természetes szám, pitagoraszi számhármast alkot, ha fennáll:x2 == y2 + z2.

A következő két függvényben kétféleképpen is megadjuk a generálási szabályt, illetve a feltételeket, ahol a második módszer lesz a hatékonyabb.

pitagorasz :: (Num a, Enum a, Ord a) => a -> [(a, a, a)]

pitagorasz n = [(x, y, z) | x <- [1..n], y <- [1..n], z <- [1..n], x * x == y * y + z * z, y < z]

pitagorasz_ :: (Num a, Enum a, Eq a) => a -> [(a, a, a)]

pitagorasz_ n = [(x, y, z) | x <- [1..n], y <- [1..n], z <- [y + 1..n], x * x == y * y + z * z]

> pitagorasz 17

[(5,3,4),(10,6,8),(13,5,12),(15,9,12),(17,8,15)]

4.16. feladat Írjunk egy Haskell-függvényt, amely kiválasztja egy listából a négyzetszámokat.

valasztN :: Integral a => [a] -> [a]

valasztN ls = [i | i <- ls, negyzetV i]

negyzetV :: Integral a => a -> Bool negyzetV x = temp * temp == x

where

4.7. Halmazkifejezések 55

temp = truncate (sqrt (fromIntegral x))

> valasztN [12, 144, 200, 196, 154, 9, 8, 4, 6, 100, 625]

[144,196,9,4,100,625]

A négyzetszámok vizsgálatát anegyzetVfüggvény végzi, amely előbb négy-zetgyököt számol a bemeneti paraméteren, majd a truncate függvénnyel levágja a tizedesjegyeket, és az így kapott szám négyzetéről vizsgálja meg, hogy egyenlő-e a bemenettel. Azsqrtbemenete FloatvagyDouble típu-sú, ezért a fromIntegralfüggvénnyel típuskonverziót hajtottunk végre.

4.17. feladat Írjunk egy Haskell-függvényt, amely a bemeneti lista elemeit kétfelé válogatja, meghatározza egy listába a prímszámokat, egy másikba pedig az összetett számokat.

import Data.List

valogatNr :: (Integral a) => [a] -> ([a], [a]) valogatNr ls = (primL, osszL)

where

primL = [i | i <- ls, primT 3 i]

osszL = ls \\ primL

> valogatNr [24, 97, 5, 11, 74, 41, 61, 19, 100]

([97,5,11,41,61,19],[24,74,100])

A kért listákat előállító főfüggvény a valogatNr lesz, amelyben a primT segítségével határoztuk meg a prímszámokat. AprimT-nek két pa-ramétere van, a másodikról vizsgálja, hogy prímszám-e úgy, hogy az első paraméterrel való oszthatóságot figyeli.

primT :: (Integral a) => a -> a -> Bool primT k nr

| nr <= 1 = error "hibas bemenet"

| nr == 2 = True

| even nr = False

| nr < k * k = True

| mod nr k == 0 = False

| otherwise = primT (k + 2) nr

> primT 3 1789 True

> primT 3 100 False

AprimTelső három feltétele a triviális eseteket kezeli. Azeven könyv-tárfüggvény, amelynekTruea visszatérési értéke, ha a bemenet páros szám, ellenkező esetbenFalse. A negyedik feltétel akkor teljesül, ha a szám négy-zetgyökéig nem találtunk páratlan osztót, ebben az esetben True lesz a kimenet értéke, mert a szám prímszám. A négyzetgyök vizsgálata nem exp-licit módon történik, hanem helyette megvizsgáljuk, hogy az osztó négyzete nem nagyobb-e a számnál. Az ötödik feltételben kerül sor az oszthatóság vizsgálatára, amely ha fennáll, akkor Falselesz a függvény kimeneti érté-ke, mert a szám összetett. Azotherwiseágban a rekurzív függvényhívásra kerül sor. Itt figyeljük meg, hogy akértékét kettesével léptetjük, mert a ha-tékonyság miatt az oszthatóságot csak páratlan számokra teszteljük, a páros bemenetet ugyanis egy korábbi feltételben kezeltük. Ahhoz, hogy páratlan számokkal végezhessük az oszthatóságot, a primTfüggvény első paraméte-rének kezdőértékét3-ra állítottuk.

AvalogatNrfüggvényben aprimLlistába azokat a számokat tesszük, amelyek prímszámok. AzosszLaz összetett számok listáját jelöli, ahol két lista különbségének a meghatározásához az \\operátort használtuk.

A \\operátor aData.Listkönyvtármodulban van, tetszőleges elem-típusú listákon alkalmazható. Az eredeti listában a megadott elemek első előfordulását törli, meghagyva a többi előfordulási értéket. Figyeljük meg, hogyan kell eljárni, ha egy könyvtárcsomagból csak egy függvényt szeret-nénk használni. A következő lekérdezések előtt a \\ használatához nem importáljuk a teljes könyvtárcsomagot.

> import Data.List ( (\\) )

> [45, 7, 8, 12, 3, 9, 10] \\ [45, 10, 12]

[7,8,3,9]

> [45, 7, 8, 12, 45, 3, 45, 9, 10] \\ [45, 10, 12]

[7,8,45,3,45,9]

> "Nyugati-Karpatok" \\ "aeiou"

"Nygt-Karpatk"