Szóláncolatok építése

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!

A feladatok egymásra épülnek ezért érdemes ezeket a megadásuk sorrendjében megoldani, de legalább megérteni az aktuális feladatot megelőző feladatokat! A függvények definíciójában lehet, sőt javasolt is alkalmazni a korábban definiált függvényeket (függetlenül attól, hogy sikerült-e azokat megadni).

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

Keresünk négybetűs szavak esetén egy olyan levezetést, amelyben el tudunk jutni egy szóból egy másikba csupán lépésenkénti egyetlen karakter megváltoztatásával. A megoldás során ügyelni fogunk arra, hogy a megoldásban felhasznált szavak mindegyike pontosan négybetűs legyen, illetve az eredményként kapott levezetés mindig egy adott szótár szerint jöjjön létre.

A szavak hosszának megtartásához ezért létrehozzuk a Word típust, amelyet egy rendezett négyesként ábrázolunk:

type Word = (Char, Char, Char, Char)

String konvertálása a Word típusra (1 pont)

Készítsük el azt a függvényt, amelyik lehetővé teszi, hogy pontosan négy karakterből álló láncokat a Word típusra alakítsuk! Amennyiben a bemenet nem megfelelő, a függvénynek nincs értéke; ezt az error alkalmazásával lehet jelezni.

fromString :: String -> Word

Test>
('A', 'B', 'C', 'D') :: Word
Test>
⊥₁ :: Word
⊥₁: Invalid input
CallStack (from HasCallStack):
  error, called at ./WordSolver.lhs:59:18 in main:WordSolver
Test>
⊥₁ :: Word
⊥₁: Invalid input
CallStack (from HasCallStack):
  error, called at ./WordSolver.lhs:59:18 in main:WordSolver

Word visszaalakítása String értékre (1 pont)

Adjuk meg azt a függvényt, amellyel Word értékekből tudunk visszanyerni karakterláncokat!

toString :: Word -> String

Test>
"FPfp" :: String

Szavak távolsága (2 pont)

Írjunk egy függvényt, amellyel Word típusú értékként ábrázolt (négybetűs) szavak távolságát tudjuk meghatározni!

Két szó távolságát úgy tudjuk meghatározni, hogy összeszámoljuk az egyes pozíciókban eltérő karaktereket. Ennek megfelelően egy szó saját magától mért távolsága mindig 0, valamint két teljesen különböző szó távolsága 4. A kis- és nagybetűket megkülönböztetjük.

wordDistance :: Word -> Word -> Int

Test>
0 :: Int
Test>
1 :: Int
Test>
2 :: Int
Test>
4 :: Int

Szavak egymásra következése (1 pont)

Állapítsuk meg egy függvény segítségével, hogy két szó következhet-e egymás után!

Ezt akkor mondjuk, ha a két szó távolsága pontosan egy. Értelemszerűen ezzel kizárjuk, hogy egy szó saját maga után következzen, ami a későbbiekben fontos lesz.

isAdjacent :: Word -> Word -> Bool

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

Szavak leválogatása rákövetkezés szerint (2 pont)

Határozzuk meg, hogy egy adott szóhoz viszonyítva szavak egy listájából mely elemek tekinthetőek rákövetkezőnek és melyek nem!

Ebben az esetben egy párt kell létrehoznunk, amelynek első komponense az adott szóra rákövetkezőeket tartalmazza egy listában, a második komponense pedig az eredeti lista maradékát. Tehát az így keletkező két lista konkatenációja mindig a kiindulási lista elemeit tartalmazza.

adjacentWords :: Word -> [Word] -> ([Word], [Word])

Test>
([('A', 'A', 'A', 'B')], [('A', 'B', 'A', 'B'), ('B', 'B', 'B', 'B')]) :: ([Word], [Word])
Test>
([], [('A', 'a', 'a', 'A'), ('A', 'a', 'a', 'a'), ('a', 'a', 'a', 'A')]) :: ([Word], [Word])
Test>
([('A', 'B', 'A', 'A'), ('C', 'A', 'A', 'A')], []) :: ([Word], [Word])
Test>
([], [('A', 'A', 'A', 'A'), ('B', 'B', 'B', 'B')]) :: ([Word], [Word])

Egy lista lehetséges forgatásai (2 pont)

Hozzuk létre egy lista összes lehetséges forgatását!

Például az [1,2,3] lista esetén egymásra következő elforgatásaikkal tudjuk megadni annak összes elforgatását:

[1,2,3] --> [2,3,1] --> [3,1,2]

Ennek megfelelően egy lista elforgatottjának tekintjük a lista elemeinek azon permutációját, ahol az elemek megegyező sorrendben vannak, azonban pozíciójuk szerint (valamennyivel) balra eltolva. Az eltolás során kiszoruló elemeket a lista végén hozzuk vissza.

rotations :: [a] -> [[a]]

Test>
[[1, 2, 3], [2, 3, 1], [3, 1, 2]] :: [[Int]]
Test>
["alma", "lmaa", "maal", "aalm"] :: [[Char]]
Test>
[[True, False], [False, True]] :: [[Bool]]
Test>
[[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], [2, 3, 4, 5, 6, 7, 8, 9, 10, 1], [3, 4, 5, 6, 7, 8, 9, 10, 1, 2], [4, 5, 6, 7, 8, 9, 10, 1, 2, 3], [5, 6, 7, 8, 9, 10, 1, 2, 3, 4], [6, 7, 8, 9, 10, 1, 2, 3, 4, 5], [7, 8, 9, 10, 1, 2, 3, 4, 5, 6], [8, 9, 10, 1, 2, 3, 4, 5, 6, 7], [9, 10, 1, 2, 3, 4, 5, 6, 7, 8], [10, 1, 2, 3, 4, 5, 6, 7, 8, 9]] :: [[Int]]

Optimális érték kiválasztása célfüggvény szerint (2 pont)

Készítsünk egy olyan függvényt, amely a paraméterként megadott célfüggvény alapján kiválasztja egy lista elemeinek egyikét! Jelezzük az error függvénnyel, ha valamiért ezt a választást nem tudjuk megtenni.

A célfüggvényünk a lista elemeihez hozzárendel egy egész számot. A feladatunk az, hogy válasszuk ki azt az elemet, amelyhez elsőként a legkisebb értéket rendeltük ezzel a függvénnyel.

findOptimalBy :: (a -> Int) -> [a] -> a

Test>
'c' :: Char
Test>
"jövő" :: [Char]
Test>
[1] :: [Integer]
Test>
⊥₁ :: Int
⊥₁: No optimum
CallStack (from HasCallStack):
  error, called at ./WordSolver.lhs:176:24 in main:WordSolver

Szavak optimális transzformációs sorozatának keresése két szó között (4 pont)

Készítsünk egy függvényt, amellyel meg tudjuk adni, hogy egy kezdőszóból indulva egy adott szótár felhasználásával milyen módon érhető el a megadott végszó, amennyiben az lehetséges!

Az eredményként előállítandó szósorozat tagjaira páronként igaznak kell lennie, hogy a köztük levő (szó)távolság pontosan egy. Kivéve természetesen, ha a kezdőszó és a végszó megegyezik. A megoldás lehetőleg ne tartalmazzon ismétlődő részleteket!

A megvalósítás során az alábbi egyszerű eseteket célszerű vizsgálni:

Ha ezek közül egyik sem áll fenn, akkor folytassuk a megoldás keresését a következők szerint:

solve :: [Word] -> Word -> Word -> [Word]

Test>
["0000"] :: [String]
Test>
["0000", "1000"] :: [String]
Test>
["0000", "0001", "0011", "0111", "1111"] :: [String]
Test>
["0000", "0100", "1100", "1101", "1111"] :: [String]
Test>
["0101", "0001", "0000", "0010", "1010"] :: [String]
Test>
[] :: [String]
binaries = map fromString
   [ "0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111"
   , "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111" ]
someBinaries = map fromString
   [ "0000", "0001", "0010", "0100", "1000", "1100", "1101", "1111" ]

Pontozás (elmélet + gyakorlat)