Binárisan kódolt decimálisok

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 (Összesen: 20 pont)

Ebben a feladatban olyan függvényekkel foglalkozunk, amelyek nemnegatív egész számok bináris ábrázolását, valamint annak tömörített binárisan kódolt decimális (packed Binary-Coded Decimal), röviden BCD alakját képesek kiszámítani. A tömörített BCD jellemzője, hogy négybites csoportokban, tizes számrendszerbeli számjegyekkel írjuk fel a szám értékét. Ezért például a 243 a 2, 4 és 3 decimális számjegyeknek megfelelő bitsorozatokból tevődik össze, mint 0010 (2), 0100 (4) és 0011 (3). Ezért tehát a neki megfelelő BCD kódolású alak 0010 0100 0011.

Egész szám bináris ábrázolása (2 pont)

Írjunk egy olyan függvényt, amely egy nemnegatív egész számnak meghatározza a bináris (kettes számrendszerbeli) alakját 0 és 1 értékek listájaként! Az ilyen listákat az szám értéknek megfelelő bitsorozatnak tekintjük. A számítás során a lista elején helyezkednek a magasabb helyiértékű számjegyek.

toBinary :: Integer -> [Int]

Test>
[] :: [Int]
Test>
[1, 1, 0] :: [Int]
Test>
[1, 1, 0, 1] :: [Int]
Test>
[1, 0, 1, 0, 1, 0, 1] :: [Int]
Test>
[1, 1, 1, 1, 1, 1, 1] :: [Int]
Test>
[1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1] :: [Int]
Test>
[1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0] :: [Int]

Bináris ábrázolásból egész szám meghatározása (2 pont)

Készítsünk egy olyan függvényt, amely egy nemnegatív egész számot 0 és 1 értékek listájaként (vagyis bitsorozatként) megadott alakjából kiszámít! A számítás során feltételezhetjük, hogy a lista elején helyezkednek el a magasabb helyiértékű számjegyek.

fromBinary :: [Int] -> Integer

Test>
0 :: Integer
Test>
2 :: Integer
Test>
5 :: Integer
Test>
85 :: Integer
Test>
127 :: Integer
Test>
2619 :: Integer
Test>
5938 :: Integer

A BCD ábrázoláshoz szükséges bitek számának megadása (1 pont)

Adjuk meg azt a függvényt, amely az alábbi képlet alapján képes meghatározni, hogy egy binárisan n biten ábrázolt szám esetén hány bitet foglalna a neki megfelelő értékű, tömörített BCD-szám!

S(n) = 4 ⋅ ⌈log10 2n

size :: Int -> Int

Test>
8 :: Int
Test>
12 :: Int
Test>
20 :: Int
Test>
40 :: Int

Segítségül: A log10 x értéke a logBase függvénnyel állapítható meg.

Lista kiegészítése balról (1 pont)

Definiáljunk egy olyan függvényt, amely egy adott listát balról kiegészít szintén adott mennyiségű, adott értékű elemmel!

padLeft :: Int -> a -> [a] -> [a]

Test>
[0, 0, 0, 0, 1, 0, 1, 1] :: [Integer]
Test>
[0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1] :: [Integer]
Test>
[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1] :: [Integer]
Test>
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0] :: [Integer]
Test>
"___foobar" :: [Char]
Test>
" foobar" :: [Char]

Bitek eltolása balra (1 pont)

Készítsük el a biteket balra eltolni képes függvényt! Ez egy infix operátor, amelynek első operandusa az a lista, amely 0 és 1 értékeket tartalmaz és bitek sorozataként értelmezünk, a második operandusa pedig az eltolás mértéke. Az eltolás során minden bitet az eredeti pozíciójához képest eggyel balra mozgatunk úgy, hogy a sorozat bal szélén levő bit kiesik, és a sorozat jobb szélén a kieső bitet egy nullás értékű bit pótolja. Ezért ügyeljünk arra, hogy eredményként mindig egy bemenettel egyező hosszúságú listát építsük fel!

infixl 8 <<
(<<) :: [Int] -> Int -> [Int]

Test>
[] :: [Int]
Test>
[0, 1, 0] :: [Int]
Test>
[1, 0, 0] :: [Int]
Test>
[0, 0, 0] :: [Int]
Test>
[0, 0, 0] :: [Int]
Test>
[1, 1, 0, 1, 0, 0, 0, 0] :: [Int]

Összeadás bináris ábrázolásban (2 pont)

Definiáljunk egy olyan függvényt, amely megadja két bitsorozatként ábrázolt szám összegét, vagyis elvégzi két bináris szám összeadását! Emlékezzünk, ennek során gondoskodnunk kell majd az átvitel kezeléséről. Valamint arról, hogy a listák hossza eltérő lehet. Ez utóbbi esetben célszerű a rövidebb listát mindig nullákkal kiegészíteni.

A számolás menetét röviden összefoglaljuk a következő példán keresztül. Például vegyük az 13 és a 3 értékeknek megfelelő bitsorozatok, azaz a 1101 és az 11 összeadását:

átvitel:      1 1 1 1
              . 1 1 0 1
            + . . . 1 1
            -----------
eredmény :    1 0 0 0 0

Megjegyzés: A feladatot a fromBinary és toBinary függvények alkalmazása nélkül szabad csak megoldani!

infixl 6 .+.
(.+.) :: [Int] -> [Int] -> [Int]

Test>
[] :: [Int]
Test>
[1, 0, 1] :: [Int]
Test>
[1, 0, 1] :: [Int]
Test>
[1, 0, 1, 1] :: [Int]
Test>
[1, 0, 0, 0] :: [Int]
Test>
[1, 0, 0, 0, 0] :: [Int]
Test>
[1, 1, 0, 0, 1, 0, 1] :: [Int]

Egy lista adott méretű darabokra osztása (2 pont)

Készítsünk egy olyan függvény, amellyel az első paraméterében megadott méretű darabokra tudunk osztani egy (másik paraméterében megadott) listát! Amennyiben a lista már nem tartalmaz adott mennyiségű elemet, úgy tegyük az így keletkező sorozat végére a lista összes fennmaradó elemét. A méretről feltételezhetjük, hogy legalább egy.

chunksOf :: Int -> [a] -> [[a]]

Test>
[] :: [[()]]
Test>
[[1, 2, 3], [4, 5, 6], [7, 8, 9]] :: [[Integer]]
Test>
[[20, 19, 18, 17], [16, 15, 14, 13], [12, 11, 10, 9], [8, 7, 6, 5], [4, 3, 2, 1]] :: [[Integer]]
Test>
["foob", "ar"] :: [[Char]]
Test>
["fo", "ob", "ar"] :: [[Char]]
Test>
["f", "o", "o", "b", "a", "r"] :: [[Char]]

Bináris számok álakítása binárisan kódolt decimálisra

A bitsorozatok binárisan kódolt decimálisra alakításához az alábbi lépésekből álló (ún. “double dabble”) algoritmust alkalmazzuk. Az egyes lépések szemléltetéséhez a 243 decimális szám bináris alakját (11110011) fogjuk kiszámolni.

  1. Tegyük fel, hogy az átalakítani kívánt számot n biten ábrázoltuk binárisan. Így a számnak megfelelő bitsorozatot egészítsük ki balról (padLeft) annyi nullával, amekkora méretű (size n) a neki megfelelő BCD-szám lenne. Ez 243 esetében 12 további bitet jelent, amelyeket most az olvashatóság érdekében négyes csoportokra tagoltunk.

      0000 0000 0000 11110011
  2. Ezután a következő ciklust futtassuk le pontosan n alkalommal:

    1. Ellenőrizzük, hogy a BCD-számjegyek között van-e olyan, amelyik túlcsordul, azaz négynél (az 0100 értéknél) nagyobb. Ha van ilyen a sorozatban, akkor növeljük meg hárommal (az 0011 értékkel). (Ezt a lépést minden iterációs lépésben csak egyszer kell elvégezni. Az ellenőrzés során vegyük észre, hogy a chunksOf függvény alkalmas lehet a BCD-számjegyek szétbontására.)

    2. Toljuk el az összes bitet balra (<<).

    A példák esetében ez a ciklus a következőképpen fog működni:

      1. iteráció:
      0000 0000 0000 11110011  (nincs túlcsorduló érték, balra léptet)
      2. iteráció:
      0000 0000 0001 11100110  (nincs túlcsorduló érték, balra léptet)
      3. iteráció:
      0000 0000 0011 11001100  (nincs túlcsorduló érték, balra léptet)
      4. iteráció:
      0000 0000 0111 10011000  (az 111 túlcsordult, növeljük hárommal)
      0000 0000 1010 10011000  (balra léptet)
      5. iteráció:
      0000 0001 0101 00110000  (a 0101 túlcsordult, növeljük hárommal)
      0000 0001 1000 00110000  (balra léptet)
      6. iteráció:
      0000 0011 0000 01100000  (nincs túlcsorduló érték, balra léptet)
      7. iteráció:
      0000 0110 0000 11000000  (a 0110 túlcsordult, növeljük hárommal)
      0000 1001 0000 11000000  (balra léptet)
      8. iteráció:
      0001 0010 0001 10000000  (nincs túlcsorduló érték, balra léptet)
      0010 0100 0011 00000000  (végeredmény)
  3. Az eredmény a bitsorozat első annyi eleme lesz, amennyi biten ábrázoljuk a bináris számhoz tartozó BCD-értéket (size n). A példánk esetében ez 001001000011 (2-4-3).

Az iteráció egy lépése (3 pont)

Valósítsuk meg egy függvény formájában a fentebb leírt algoritmus iterációs részének egy lépését!

step :: Int -> [Int] -> [Int]

Test>
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0] :: [Int]
Test>
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0] :: [Int]
Test>
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0] :: [Int]
Test>
[0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0] :: [Int]
Test>
[0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0] :: [Int]
Test>
[0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0] :: [Int]
Test>
[0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0] :: [Int]
Test>
[0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0] :: [Int]

Bináris ábrázolású egész szám BCD alakra hozása (3 pont)

A előbbi, step nevű függvény felhasználásával adjunk meg egy függvényt, amely a teljes algoritmust végrehajtja egy bemenetként megadott, bináris formában ábrázolt nemnegatív egész számon!

binaryToBCD :: [Int] -> [Int]

Test>
[0, 0, 0, 1, 0, 1, 0, 1] :: [Int]
Test>
[0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1] :: [Int]
Test>
[0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0] :: [Int]

Binárisan kódolt decimális átalakítása egész számmá (3 pont)

Az átalakítás eredményének egyszerűbb ellenőrzéséhez írjunk egy olyan függvényt, amely egy tömörített BCD alakban tárolt számnak megadja az értékét egész számként! Figyeljük meg, hogy az egyes BCD-számjegyek kiemelésére alkalmas lehet a chunksOf függvény.

fromBCD :: [Int] -> Integer

Test>
5 :: Integer
Test>
35 :: Integer
Test>
935 :: Integer
Test>
243 :: Integer
Test>
65244 :: Integer

Pontozás