Értelmező

Használható segédanyagok: Haskell könyvtárainak dokumentációja, Hoogle, a tárgy honlapja és a BE-AD rendszerbe feltöltött beadandók. Ha bármi kérdés, észrevétel felmerül azt a gyakorlatvezetőnek jelezzétek, NE a mellettetek ülőnek!

A feladat összefoglaló leírása

A feladatok egymásra épülnek, ezeket lehetőleg megadás sorrendjében oldjátok meg!

Adott egy nagyon egyszerű programozási nyelv. A benne írt programokat egy absztrakt számítógépen tudjuk futtatni, amely a következő részekből áll:

Az utasítások pedig a következőek:

Feladatunk egy hozzá tartozó értelmező egyes részeinek elkészítése. Észrevehetjük, hogy a ‘[’* és *’]’ szimbólumok segítségével ciklusokat tudunk megfogalmazni, amelyeket természetesen akár egymásba is tudunk ágyazni.

A memória kezdőállapota (1 pont)

Adjuk meg azt a konstanst, amely leírja az absztrakt gép memóriájának kezdőállapotát! A memóriát különleges módon, két lista párjaként ábrázoljuk: az első azokat az elemeket tartalmazza, amelyek a memória mutatója előtt szerepelnek, a második pedig azokat, amelyek utána.

A kezdőállapotban a memória elejére mutatunk (tehát semmilyen elem nincs a mutató előtt), és minden elem nulla értékű.

emptyData :: ([Int],[Int])

Test>
0 :: Int
Test>
[] :: [Int]

Olvasás a memória mutatott pozíciójáról (1 pont)

Valósítsuk meg azt a műveletet, amellyel olvasni tudunk a memóriából arról a pozícióról, amelyre mutatunk! Tehát a mutató előtt levő első értéket kell visszaadnunk.

readPtr :: ([a],[a]) -> a

Test>
0 :: Int
Test>
4 :: Integer

A memóriabeli mutató léptetése előre (1 pont)

Valósítsuk meg azt a műveletet, amellyel a memóriában előrébb tudjuk léptetni a mutatót!

ptrInc :: ([a],[a]) -> ([a],[a])

Test>
[0] :: [Int]
Test>
0 :: Int

A memóriabeli mutató léptetése vissza (1 pont)

Valósítuk meg azt a művelete, amellyel a memóriában visszább tudjuk léptetni a mutatót!

ptrDec :: ([a],[a]) -> ([a],[a])

Test>
([], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …, ……]) :: ([Int], [Int])
Test>
2 :: Integer

Írás a memória mutatott pozíciójára (2 pont)

Adjuk meg azt a függvényt, amellyel a memóriában mutatott helyre tudunk írni egy értéket! A mutató értéke ekkor nem változik.

Fontos viszont, hogy a rögzített érték csak 0 és 255 közé eshet (mivel 8 bites, előjel nélküli értékeket tárolunk a memóriában)! Ha az érték kilépne ebből az intervallumból, akkor ,,körbefordul’’. Így például 256-ból 0 lesz, -42-ből pedig 214.

writePtr :: Integral a => ([a],[a]) -> a -> ([a],[a])

Test>
8 :: Integer
Test>
0 :: Int

Egy lista minden n-edik elemének a kiválogatása (1 pont)

Készítsünk egy olyan függvényt, amely egy adott paraméternek megfelelően kiveszi egy lista valahanyadik elemeit! Vigyázzunk, a függvénynek a válogatást az első elemtől kell kezdenie, tehát a lista első eleme mindig része az eredménynek (amennyiben létezik)!

every :: Int -> [a] -> [a]

Test>
[1, 3, 5, 7, 9] :: [Integer]
Test>
[5, 10, 15, 20] :: [Integer]

A memóriában mutatott érték módosítás függvénnyel (2 pont)

Adjuk meg azt a függvényt, amellyel egy egyparaméteres függvény felhasználásával módosítani tudjuk a memória mutatott pozícióján található értéket! A mutató értéke nem változik.

modifyPtr :: Integral a => (a -> a) -> ([a],[a]) -> ([a],[a])

Test>
1 :: Int
Test>
3 :: Int

Egy ciklus törzsének levágása (3 pont)

Készítsük el azt a függvényt, amely megkeresi egy ciklus végét jelző ‘]’ szimbólumot, és annak elhagyásával kettévágja a parancsok sorozatát! Figyeljünk rá, hogy a ciklus végét mindig az adott parancssorozat végétől kezdve visszafele keressük!

getLoopBody :: String -> (String,String)

Test>
("+++++.", "<<>>++--") :: (String, String)
Test>
("+.][+.]+.", "--..") :: (String, String)

Feltételes iterálás (4 pont)

Készítsünk egy függvényt, amely kap egy logikai értékű függvényt, egy egyparaméteres függvényt, illetve egy egész számot, n-et, valamint egy kezdőértéket.

Működése a következő: a kezdőértékből kiindulva alkalmazzuk az egyparaméteres függvényt egészen addig, amíg a részeredmények közül csak minden n-ediket véve nem találunk egy olyan elemet, amelyre a logikai értékű függvény igazat ad.

loopUntil :: (a -> Bool) -> (a -> a) -> Int -> a -> a

Test>
12 :: Integer
Test>
192 :: Integer

Parancsok végrehajtása (5 pont)

Ebben a részfeladatban az értelmezés lényegét végző run függvényt kell elkészítenünk. Ennek a dolga az egyes utasítások végrehajtása. A végrehajtása egy egyszerű függvénnyel fogjuk modellezni, amely mindig egy kiinduló állapotból hoz létre egy rákövetkező állapotot.

A program futása során az állapotot, vagyis a run paraméterét és eredményét, egy rendezett párral adjuk meg:

A függvény feladata tehát az, hogy a bevezetésben megadott parancsokat felismerje a parancsok sorozatában, és eredményként egy olyan állapotot hozzon létre, amely a nekik megfelelő módosításokat tartalmazza a paraméterül kapott állapoton.

Ha nincs már több parancs, vagy nem ismert a parancs, akkor ne változtasson semmit sem az állapoton.

Segítség: Nyugodtan alkalmazzuk a korábban definiált függvényeket! A ciklusok magját érdemes először kivágni, és külön végrehajtani és az eredményét hozzáfűzni az aktuális állapothoz.

run :: (([Int],[Int]),(String,String)) -> (([Int],[Int]),(String,String))

Test>
(([1, 2, 3], [5, 5, 6]), ("+.", [])) :: (([Int], [Int]), (String, String))
Test>
(([], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ……]), ([], [])) :: (([Int], [Int]), (String, String))
Test>
"\SOH\ETX\STX" :: String

Programok értelmezése (2 pont)

Írjuk meg az értelmező működtetéséhez szükséges felső szintű függvényt! A feladata lényegében a run függvény meghívása a megfelelő kezdőállapottal, valamint megállítása akkor, amikor már elfogytak a feldolgozandó parancsok.

interpret :: String -> String

Test>
"\SOH\STX\ETX" :: String
Test>
"\NUL\SOH\STX\ETX\EOT\ENQ\ACK\a\b\t\n\v\f\r\SO\SI\DLE\DC1\DC2\DC3\DC4\NAK\SYN\ETB\CAN\EM\SUB\ESC\FS\GS\RS\US !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~\DEL\128\129\130\131\132\133\134\135\136\137\138\139\140\141\142\143\144\145\146\147\148\149\150\151\152\153\154\155\156\157\158\159 ¡¢£¤¥¦§¨©ª«¬\173®" ++ […, ……] :: String

Pontozás