Najbliżej mogę myśleć byłoby coś
λ> data Bin = LSB | Zero Bin | One Bin
λ| -- deriving Show
To sprawia, że możliwe jest skonstruowanie liczb binarnych robi tylko
λ> One . One . Zero . Zero . One . One $ LSB
One (One (Zero (Zero (One (One LSB)))))
Można by również wyobrazić funkcją dekodowania pracy na zasada (znacznie lepsza wersja sugerowana przez Ingo w komentarzach)
λ> let toInt :: (Integral a) => Bin -> a
λ| toInt = flip decode 0
λ| where decode :: (Integral a) => Bin -> a -> a
λ| decode LSB value = value
λ| decode (Zero rest) value = decode rest (2*value)
λ| decode (One rest) value = decode rest (2*value + 1)
Który może być następnie użyty do dekodowania liczby binarnej na liczbę całkowitą.
λ> toInt (Zero . One . One . One . Zero . Zero . One $ LSB)
57
Trudność z tym, co chcesz osiągnąć to, że trzeba czytać liczby binarne „inside out” lub tak powiem. Aby poznać wartość najbardziej znaczącej cyfry, musisz wiedzieć, ile cyfr masz w numerze. Gdybyś zapisywał swoje liczby binarne w "odwrocie" - tj. Najbardziej zewnętrzna cyfra jest najmniej znaczącą cyfrą, wtedy rzeczy byłyby dużo łatwiejsze w obsłudze, ale liczby wyglądałyby wstecz po ich utworzeniu i wydrukowaniu za pomocą domyślnej instancji z Show
.
Przyczyną tego nie jest problem z liczbami jednoznacznymi, ponieważ nie ma "najmniej znaczącej cyfry", ponieważ wszystkie cyfry mają tę samą wartość, więc można przeanalizować liczbę z obu kierunków, a otrzymasz taki sam wynik.
Dla kompletności, tutaj jest to samo, ale z cyfrą najbardziej zewnętrzna jest najmniej znacząca cyfra:
λ> data Bin = MSB | Zero Bin | One Bin
λ| -- deriving Show
To wygląda prawie jak poprzednio, ale można zauważyć, że gdy funkcja dekodowania jest zaimplementowane,
λ> let toInt = flip decode (1,0)
λ| where
λ| decode (One rest) (pos, val) = decode rest (pos*2, val+pos)
λ| decode (Zero rest) (pos, val) = decode rest (pos*2, val)
λ| decode MSB (_, val) = val
Liczby są zapisane wstecz!
λ> toInt (Zero . Zero . Zero . One . Zero . One $ MSB)
40
Jest to jednak o wiele łatwiejsze w obsłudze. Możemy na przykład dodać dwie liczby binarne osobno dla każdego przypadku. (Uwaga: wiele przypadków)
λ> let add a b = addWithCarry a b False
λ| where
λ| addWithCarry :: Bin -> Bin -> Bool -> Bin
λ| addWithCarry MSB MSB True = One MSB
λ| addWithCarry MSB MSB False = MSB
λ| addWithCarry MSB b c = addWithCarry (Zero MSB) b c
λ| addWithCarry a MSB c = addWithCarry a (Zero MSB) c
λ| addWithCarry (Zero restA) (Zero restB) False = Zero (addWithCarry restA restB False)
λ| addWithCarry (One restA) (Zero restB) False = One (addWithCarry restA restB False)
λ| addWithCarry (Zero restA) (One restB) False = One (addWithCarry restA restB False)
λ| addWithCarry (One restA) (One restB) False = Zero (addWithCarry restA restB True)
λ| addWithCarry (Zero restA) (Zero restB) True = One (addWithCarry restA restB False)
λ| addWithCarry (One restA) (Zero restB) True = Zero (addWithCarry restA restB True)
λ| addWithCarry (Zero restA) (One restB) True = Zero (addWithCarry restA restB True)
λ| addWithCarry (One restA) (One restB) True = One (addWithCarry restA restB True)
W tym momencie dodawania dwóch liczb binarnych jest bardzo proste:
λ> let forty = Zero . Zero . Zero . One . Zero . One $ MSB
λ| eight = Zero . Zero . Zero . One $ MSB
λ|
λ> add forty eight
Zero (Zero (Zero (Zero (One (One MSB)))))
I rzeczywiście!
λ> toInt $ Zero (Zero (Zero (Zero (One (One MSB)))))
48
+1, tylko za tytuł :) –
Rozdział 9 "Strukturalnie funkcjonalnych struktur danych" autorstwa Chrisa Okasaki wygląda na "Reprezentacje numeryczne" z naciskiem na liczby binarne. Może uda Ci się uzyskać wystarczająco dobry podgląd książki w Książkach Google, aby uzyskać wskazówki do pokrewnej pracy. –