Reputation: 21
I need to convert a string of chars to a list of binary numbers in Haskell. I've written two functions to do this, but I'm not sure how to combine them into one. So far I have
dec[]=[]
dec(x:xs) = ord(x): dec xs
to convert every char in the list into a decimal number. The next function
bin 0 = [0]
bin n| n `mod` 2 == 1 = bin (n `div` 2) ++ [1]
| n `mod` 2 == 0 = bin (n `div` 2) ++ [0]
converts a decimal number to its binary equivalent. I'm not sure how to apply the second function to every element in the list, in order to convert every char to its equivalent in binary. I tried to use the where
clause:
where n = dec(x:xs) = ord(x): dec xs
but this is not valid as there are two equals signs on the same line. How can I achieve the correct functionality?
Upvotes: 2
Views: 2440
Reputation: 12749
You can be pretty certain that an Int
will be stored in binary. It only appears to be in decimal because it is converted to decimal when you print it. So, the name dec
is a misnomer, that function is converting a String
into a sequence of numbers that represent the Unicode value of each character. You can avoid explicit recursion by using map:
toUnicode :: String -> [Int]
toUnicode = map ord
Note that this function uses so-called point-free style. The expected argument is missing, but will be passed to map
when supplied by the caller.
The Bin
function will not compile because it starts with an upper case character, making it a data constructor. You should name the function starting with a lower case character. According to your example output, you want leading zeros in your binary representations, so you can't stop conversion when the value becomes zero. You need to continue until you have converted the desired number of digits, which appears to be 8. It is also inefficient to keep appending to a list. It is better to prepend, and then reverse the result.
toBinary :: Int -> [Int]
toBinary = go 8 [] where
go 0 acc _ = reverse acc
go n acc x = go (n-1) (bit:acc) x' where
(x', bit) = x `divMod` 2
Here, we use a helper function, go
which counts down the number of remaining digits as it builds up the list of 1's and 0's.
So, now we have a function to convert a String
into a list of Int
s, and a function to convert an Int
into a list of 0/1 Int
s, and we want to glue them together to make a function that converts a String
to a list of 0/1 Int
s. If we map
our toBinary
function over the result of toUnicode
, we will get a list of lists, which must be concatenated to form a single list. This is such a common pattern that there's a function for that called, concatMap
:
stringToBinary :: String -> [Int]
stringToBinary = concatMap toBinary . toUnicode
Here we use function composition to first apply toUnicode
to the String
, and then concatMap
the toBinary
over the result.
Upvotes: 1
Reputation: 3818
What we want is a function of type String -> String
(decimal -> binary
). What you have now is
dec :: String -> [Int]
bin :: Int -> [Int] -- use *lowercase*
So it seems impossible to compose a function of type String -> String
only with these two. Besides, ord
is not what you want.
*Main> dec "123"
[49,50,51]
*Main> bin 123
[0,1,1,1,1,0,1,1]
From what you have now, the possible solution would be:
*Main Data.Char> toBinary = map intToDigit . bin . read
*Main Data.Char> toBinary "123"
"01111011"
I guess your intention may be dec :: String -> Int
, then bin . dec :: String -> [Int]
. You can follow the type signature and retry.
Upvotes: -1