Veremszámológép

Használható segédanyagok: Haskell könyvtárainak dokumentációja, (lokális!) Hoogle, a tárgy honlapja és a BE-AD rendszerbe feltöltött beadandók. Ha bármilyen kérdés, észrevétel felmerül, azt a felügyelőknek kell jelezni, NEM a diáktársaknak!

Tekintve, hogy a tesztesetek, bár odafigyelés mellett íródnak, nem fedik le minden esetben a függvény teljes működését, határozottan javasolt még külön próbálgatni a megoldásokat beadás előtt, vagy megkérdezni a felügyelőket!

Részpontszámokat csak az elégséges szint elérése után lehet kapni!

A feladat összefoglaló leírása

Ebben a feladatban egy olyan számológép implementációját készítjük el, amely egy veremmel dolgozik és a kiszámítandó aritmetikai kifejezést az ún. lengyel formában kapja meg. Ennek az a lényege, hogy a először az operandusokat, majd a műveletet adjuk meg, vagyis a kifejezést zárójelek nélkül, postfix alakban írjuk fel.

Például:

10 4 3 + 2 * -

amely tulajdonképpen a következő infix kifejezésnek felel meg:

10 - (2 * (4 + 3))

Ekkor tehát a következő lépésékben számolunk:

10 4 3 + 2 * -
10 (4 3 +) 2 * -
10 ((4 3 +) 2 *) -
(10 ((4 3 +) 2 *) -)
(10 (7 2 *) -)
(10 14 -)
-4

(Ez a típusú jelölés teljesen általánosítható, és ez adja az alapját többek közt a Forth vagy a PostScript programozási nyelveknek.)

Decimális számjegyek vizsgálata (1 pont)

Döntsük le egy karakterről, hogy decimális (tízes számrendszerbeli) számjegyet ábrázol vagy sem!

isDecimalDigit :: Char -> Bool

Test>
True :: Bool
Test>
True :: Bool

Előjel-e? (1 pont)

Döntsük el egy karakterről, hogy előjelet ábrázol vagy sem!

isSign :: Char -> Bool

Test>
True :: Bool
Test>
True :: Bool

Szöveg egész számmá alakíthatósága (2 pont)

Vizsgáljuk meg egy szóközöket nem tartalmazó karaktersorozatról, hogy egész számot ábrázol vagy sem! Az egész szám részének tekintjük az elején esetlegesen szereplő előjelet.

isInteger :: String -> Bool

Test>
False :: Bool
Test>
True :: Bool
Test>
True :: Bool
Test>
True :: Bool
Test>
False :: Bool
Test>
False :: Bool
Test>
False :: Bool

Konvertálás egész számmá (2 pont)

Készítsünk egy olyan függvényt, amellyel át tudunk alakítani egy szóközöket nem tartalmazó karaktersorozatot egész számmá! Feltételezhetjük, hogy a bemenet jólformált, vagyis az isInteger függvény igazat ad rá. Ne felejtsük el kezelni az esetlegesen előforduló előjeleket sem!

strToInteger :: String -> Integer

Test>
0 :: Integer
Test>
42 :: Integer
Test>
123456789123456789 :: Integer
Test>
-123456789123456789 :: Integer
Test>
123456789123456789 :: Integer

A verem definíciója (1 pont)

Adjuk meg, miként tudnánk egy paraméteres típusszinonímával definiálni a vermet! Ahogy a példák is jelzik, az ábrázolás alapja egy lista.


Test>
Test>
Test>

Érték lerakása a verembe (1 pont)

Készítsük el a veremhez tartozó, elem hozzáadását lehetővé tevő műveletet! A verem LIFO (Last-In-First-Out) adatszerkezet, amelybe mindig az utolsóként berakott elemet vehetjük ki elsőként.

push :: Stack a -> a -> Stack a

Test>
[1] :: Stack Integer
Test>
"!ask" :: Stack Char

Érték kivétele a veremből (1 pont)

Definiáljuk az elem kivételét egy veremből! Az eredményben a kivett elem, valamint a verem fennmaradó tartalma szerepel. Feltételezhetjük, hogy a verem legalább elemet tartalmaz.

pop :: Stack a -> (a, Stack a)

Test>
('a', "sk!") :: (Char, Stack Char)
Test>
(1, []) :: (Integer, Stack Integer)

A legutolsó két érték kivétele a veremből (1 pont)

Definiáljuk két elem egymás utáni kivételét a veremből! A visszaadott érték egy rendezett hármas, amelynek első tagja az utolsóként, a második tagja az utolsó előttiként berakott elem, végül a harmadik tagja a verem fennmaradó tartalma.

pop2 :: Stack a -> (a, a, Stack a)

Test>
('a', 's', "k!") :: (Char, Char, Stack Char)
Test>
(1, 2, []) :: (Integer, Integer, Stack Integer)

A műveletek típusa

A típusok egyszerűsítése végett megadjuk azokat a típusokat, amelyek egy egyparaméteres (IFun1) és egy kétparaméteres (IFun2), egész számokkal dolgozó függvényeket írnak le, amelyekhez hozzárendeltük a nevüket.

type IFun1 = (String, (Integer -> Integer))
type IFun2 = (String, (Integer -> Integer -> Integer))

Keresés a függvények közt (2 pont)

Készítsünk egy olyan függvényt, amellyel IFun1 vagy IFun2 típusú függvényeket tartalmazó listából tudunk név alapján visszakeresni egy elemet!

findFunction :: [(String,f)] -> String -> Maybe f

Test>
9 :: Integer
Test>
3 :: Integer

Elem feldolgozása verem használatával (4 pont)

Fogalmazzuk meg azt a függvényt, amely a számoláshoz használhat egy- és kétparaméteres függvények, a verem aktuális állapota és a bemenet következő feldolgozandó szava alapján előállítja a verem következő állapotát!

Ennek szabályai egyszerűek. Ha a bemenet következő szava:

Amennyiben a függvényalkalmazások esetében nem áll rendelkezésre elegendő paraméter a veremben, úgy jelezzünk hibát az error függvény segítségével! Illetve tegyünk hasonló módon, ha olyan művelettel találkozunk, amelyik nem ismert!

step :: ([IFun1],[IFun2]) -> Stack Integer -> String -> Stack Integer

Test>
[3] :: Stack Integer
Test>
⊥₁ :: Stack Integer
⊥₁: unknown operator
CallStack (from HasCallStack):
  error, called at ./RPN.lhs:233:43 in main:RPN
Test>
[-1, 2] :: Stack Integer
Test>
[-1, 2] :: Stack Integer
Test>
⊥₁ :: Stack Integer
⊥₁: not enough parameters
CallStack (from HasCallStack):
  error, called at ./RPN.lhs:232:43 in main:RPN
Test>
⊥₁ :: Stack Integer
⊥₁: not enough parameters
CallStack (from HasCallStack):
  error, called at ./RPN.lhs:232:43 in main:RPN

Postfix kifejezés kiértékelése adott műveletekkel (3 pont)

Az eddigi függvények felhasználásával adjuk meg, miként tudjuk kiszámítani egy postfix jelöléssel megadott kifejezés értékét úgy, hogy annak egyes elemeit egy karaktersorozat szavainak tekintjük! Vagyis feltételezzük, hogy a bemenetre karakteres formábban olyan postfix kifejezés érkezik, amelyet whitespace-szel tagoltunk.

evaluate :: ([IFun1],[IFun2]) -> String -> Integer

Test>
7 :: Integer
Test>
4 :: Integer
Test>
7 :: Integer
Test>
4 :: Integer

Számológép szabványos műveletekkel (1 pont)

Készítsük el a számológépünk egy olyan specializációját, amelyben előre rögzített műveleteink vannak! A műveletek a következők: összeadás (+), szorzás (*), kivonás (-), egész osztás (/) és maradékképzés (%), abszolút érték számítása (abs), eggyel növelés (inc) és csökkentés (dec).

rpn :: String -> Integer

Test>
7 :: Integer
Test>
4 :: Integer
Test>
7 :: Integer
Test>
3 :: Integer
Test>
10 :: Integer

Pontozás