Say there is a stadium and the row number is something like A1-10, then B1-10 and so on until ZZ
How do I make a custom data type and use it to represent the seat in Haskell?
You can think of your enumeration as being composed of three parts
a first letter,
an (optional) second letter, and
a number between 1 and 10
The first and second part both rely upon the notion of "letter", so let's define that
data Letter
= La | Lb
| Lc | Ld
| Le | Lf
| Lg | Lh
| Li | Lj
| Lk | Ll
| Lm | Ln
| Lo | Lp
| Lq | Lr
| Ls | Lt
| Lu | Lv
| Lw | Lx
| Ly | Lz
deriving ( Eq, Ord, Show )
This type is explicitly enumerated instead of merely using Char so that we don't have to worry about the differences between lower and upper case or the problems of Char containing extra things like '-' and '^'. Since I enumerated the elements in alphabetical order, the autoderived instances like Ord behave properly.
We probably do want to take advantage of the fact that Letter is a subset of Char so let's write the projections, too.
-- This one always works since every letter is a character.
letterToChar :: Letter -> Char
letterToChar l = case l of
La -> 'a'
Lb -> 'b'
Lc -> 'c'
Ld -> 'd'
Le -> 'e'
Lf -> 'f'
Lg -> 'g'
Lh -> 'h'
Li -> 'i'
Lj -> 'j'
Lk -> 'k'
Ll -> 'l'
Lm -> 'm'
Ln -> 'n'
Lo -> 'o'
Lp -> 'p'
Lq -> 'q'
Lr -> 'r'
Ls -> 's'
Lt -> 't'
Lu -> 'u'
Lv -> 'v'
Lw -> 'w'
Lx -> 'x'
Ly -> 'y'
Lz -> 'z'
-- This one might fail since some characters aren't letters. We also do
-- automatic case compensation.
charToLetter :: Char -> Maybe Letter
charToLetter c = case Char.toLower of
'a' -> Just La
'b' -> Just Lb
'c' -> Just Lc
'd' -> Just Ld
'e' -> Just Le
'f' -> Just Lf
'g' -> Just Lg
'h' -> Just Lh
'i' -> Just Li
'j' -> Just Lj
'k' -> Just Lk
'l' -> Just Ll
'm' -> Just Lm
'n' -> Just Ln
'o' -> Just Lo
'p' -> Just Lp
'q' -> Just Lq
'r' -> Just Lr
's' -> Just Ls
't' -> Just Lt
'u' -> Just Lu
'v' -> Just Lv
'w' -> Just Lw
'x' -> Just Lx
'y' -> Just Ly
'z' -> Just Lz
_ -> Nothing -- default case, no match
Now we play the same game with "digits from 1 to 10"
data Digit
= D1 | D2
| D3 | D4
| ...
deriving ( Eq, Ord, Show )
digitToInt :: Digit -> Int
digitToInt = ...
intToDigit :: Int -> Maybe Digit
intToDigit = ...
We might even write other ways of retracting an Int to a Digit. For instance, we could (1) take the absolute value of the integer and then (2) take its div and mod against 10 seats. This will result in a Digit assignment and a row number.
intToDigitWrap :: Int -> (Int, Digit)
intToDigitWrap n = (row, dig) where
(row, dig0) = n `divMod` 10
-- we use an incomplete pattern match because we have an invariant
-- now that (dig0 + 1) is in [1, 10] so intToDigit always succeeds
Just dig = intToDigit (dig0 + 1)
And the final type is easy!
data Seat = Seat { letter1 :: Letter
, letter2 :: Maybe Letter
, digit :: Digit
} deriving ( Eq, Ord, Show )
The Ord type is again completely automatically correct as Nothing is less than Show x for any x and record ordering is lexicographic. We can also write a show instance that's a bit friendlier very simply
prettySeat :: Seat -> String
prettySeat s =
let l1 = [Char.toUpper $ letterToChar $ letter1 s]
l2 = case letter2 s of
Nothing -> ""
Just c -> [Char.toUpper $ letterToChar c]
dig = show (digitToInt (digit s))
in l1 ++ l2 ++ "-" ++ dig
In all likelihood the ability to inject the Letter and Digit types into their superset types Char and Int respectively will almost certainly come in handy when writing code later.
Related
I need to swap blank space with letter from "moves" and each time I swap it I need to continue with another one from moves. I get Couldn't match expected type, even though I just want to return value x when it doesn't meet condition.
Error message:
[1 of 1] Compiling Main ( puzzlesh.hs, interpreted )
puzzlesh.hs:19:43: error:
• Couldn't match expected type ‘Int -> a’ with actual type ‘Char’
• In the expression: x
In the expression: if x == ' ' then repl x else x
In an equation for ‘eval’: eval x = if x == ' ' then repl x else x
• Relevant bindings include
eval :: Char -> Int -> a (bound at puzzlesh.hs:19:5)
repl :: forall p. p -> Int -> a (bound at puzzlesh.hs:20:5)
moves :: [a] (bound at puzzlesh.hs:16:9)
p :: t [Char] -> [a] -> [Int -> a] (bound at puzzlesh.hs:16:1)
|
19 | eval x = if x == ' ' then repl x else x
| ^
Failed, no modules loaded.
Code:
import Data.Char ( intToDigit )
sample :: [String]
sample = ["AC DE",
"FBHIJ",
"KGLNO",
"PQMRS",
"UVWXT"]
moves = "CBGLMRST"
type Result = [String]
pp :: Result -> IO ()
pp x = putStr (concat (map (++"\n") x))
p input moves = [eval x | x <- (concat input)]
where
c = 1
eval x = if x == ' ' then repl x else x
repl x count = moves !! count
count c = c + 1
I need to take character from moves, replace it onto blank space and do this till moves is []
Desired output:
ABCDE
FGHIJ
KLMNO
PQRST
UVWX
As with most problems, the key is to break it down into smaller problems. Your string that encodes character swaps: can we break that into pairs?
Yes, we just need to create a tuple from the first two elements in the list, and then add that to the result of calling pairs on the tail of the list.
pairs :: [a] -> [(a, a)]
pairs (x:tl#(y:_)) = (x, y) : pairs tl
pairs _ = []
If we try this with a string.
Prelude> pairs "CBGLMRST"
[('C','B'),('B','G'),('G','L'),('L','M'),('M','R'),('R','S'),('S','T')]
But you want a blank space swapped with the first character:
Prelude> pairs $ " " ++ "CBGLMRST"
[(' ','C'),('C','B'),('B','G'),('G','L'),('L','M'),('M','R'),('R','S'),('S','T')]
Now you have a lookup table with original characters and their replacements and the rest is straightforward. Just map a lookup on this table over each character in each string in the list.
Because you never touch any letter in the original strings more than once, you won't have to worry about double replacements.
Prelude> s = ["AC DE","FBHIJ","KGLNO","PQMRS","UVWXT"]
Prelude> r = "CBGLMRST"
Prelude> r' = " " ++ r
Prelude> p = pairs r'
Prelude> [[case lookup c p of {Just r -> r; _ -> c} | c <- s'] | s' <- s]
["ABCDE","FGHIJ","KLMNO","PQRST","UVWXT"]
I have two compare to chars:
'2' < 'a'. I typed this into my console and it returned true.
Why is that?
Because the character 2 comes before the character a in the ASCII table.
Indeed, 2's decimal code is 50, whereas a's decimal code is 97, so the latter occurs 97 - 50 = 47 characters after the former, as demonstrated by this:
iterate succ '2' !! 47
which gives back 'a'. iterate has signature (a -> a) -> a -> [a], and guess what it does, iterate f x is the infinite list [x, f x, f (f x), f (f (f x)), …], from which we take the 47th element via !! 47.
Both values, 2 and a are of type Char.
-- GHCi
:t '2' -- Char
:t 'a' -- Char
In Haskell, a Char is a 32 bit integer. So, the question is, can we use the operator < to compare these integers? Yes!
:i Char -- instance Ord Char -- Defined in `GHC.Classes'
So, Haskell says that Char can be ordered, because it is in class Ord, and we can use operators like < to compare them.
If I go on Hoogle, the Haskell version of Google, I can see the definition of the Ord class, by simply typing Ord into the search box.
(<), (<=), (>), (>=) :: a -> a -> Bool
max, min :: a -> a -> a
compare x y = if x == y then EQ
-- NB: must be '<=' not '<' to validate the
-- above claim about the minimal things that
-- can be defined for an instance of Ord:
else if x <= y then LT
else GT
x < y = case compare x y of { LT -> True; _ -> False }
x <= y = case compare x y of { GT -> False; _ -> True }
x > y = case compare x y of { GT -> True; _ -> False }
x >= y = case compare x y of { LT -> False; _ -> True }
Char is a type which is not quite UTF-32 compliant. In UTF-32, the ASCII codes are stored in the bottom 7 bits, 00 through 7F hex. In ASCII, a is 97 dec, 61 hex, and 2 is 50 dec, 32 hex and hence when we compare the 32 bit values, 2 is indeed less than a. The declared function (<) returns True.
Solved
My function isn't working properly. When I do hex2dec "100" or "10000" it gives me 16 instead of the correct 256. When I do hex2dec "101" it gives me 17 instead of the correct 257.
hex2dec :: String -> Integer
hex2dec str = go (reverse str)
where go [] = 0
go (x:xs) = hexChar x + 16 * hex2dec xs
hexChar :: Char -> Integer
hexChar c
| c == '0' = 0
| c == '1' = 1
| c == '2' = 2
| c == '3' = 3
| c == '4' = 4
| c == '5' = 5
| c == '6' = 6
| c == '7' = 7
| c == '8' = 8
| c == '9' = 9
| c == 'a' = 10
| c == 'b' = 11
| c == 'c' = 12
| c == 'd' = 13
| c == 'e' = 14
| c == 'f' = 15
| otherwise = 0
I had to change "hex2dec" to "go".
hex2dec :: String -> Integer
hex2dec str = go (reverse str)
where go [] = 0
go (x:xs) = hexChar x + 16 * go xs
Notice that your functions are partial; not all Char values are valid hex digits. Instead, define a new type to represent them:
data Hex = H0 | H1 | H2 | H3
| H4 | H5 | H6 | H7
| H8 | H9 | HA | HB
| HC | HD | HE | HF deriving (Enum)
Deriving an Enum instance gives you hexChar (let's call it hexInt) for nearly for free; we switch from Int to Integer here.
hexInt :: Hex -> Integer
hexInt = fromIntegral . fromEnum
With this in place, you can use Horner's Rule to reduce a list of Hex values to an Integer.
hex2dec :: [Hex] -> Integer
hex2dec = foldr (\d acc -> hexInt d + 16*acc) 0
To generalize this from [Hex] to String, we first define a function to convert a Char to a Maybe Hex
import Data.List
charToHex :: Char -> Maybe Hex
charToHex c = fmap toEnum $ c `elemIndex` "0123456789abcdef"
-- elemIndex returns a Maybe Int "between" Just 0 and Just 15;
-- fmap toEnum converts that to a Maybe Hex.
If you wanted to be explicit, charToHex is of course simply
charToHex '0' = Just H0
-- ...
charToHex 'f' = Just H15
charToHex _ = Nothing
Then we want a function that can handle any failures while mapping charToHex over a String.
str2dec :: String -> Maybe Integer
str2dec = fmap hex2dec . traverse charToHex
where traverse charToHex :: String -> Maybe [Hex] returns Nothing if any call to charToHex on the input string returns Nothing.
I'm trying to write a Caesar cipher but with only uppercase alphanumeric. Using ord or chr uses the whole ASCII table. How can accomplish this?
This is what I have so far:
alphabet = ['A'..'Z'] ++ ['0'..'9']
c2I = ord c - ord 'A'
i2C = chr (n + ord 'A')
the basic idea is to use mod to wrap around to the beginning.
Now it's not efficient (but hey you are using the most unsecure cipher so you might not care to much) but I'll show you using just the alphabet and indexing functions:
import Data.List (elemIndex)
alphabet :: [Char]
alphabet = ['A'..'Z'] ++ ['0'..'9']
ith :: Int -> Char
ith i = alphabet !! j
where j = i `mod` length alphabet
index :: Char -> Int
index c = case c `elemIndex` alphabet of
Just i -> i
Nothing -> error "not inalphabet"
encode :: Int -> String -> String
encode n xs = [ ith $ index x + n | x <- xs ]
this will give you
λ> encode 3 "ABCXYZ012789"
"DEF012345ABC"
now you probably will want to find a way using ord and chr - both works if you make a case distinction between A-Z and 0-9, because the ranges are:
65-90 for A-Z
48-57 for 0-9
so you cannot take a one formula without to many tricks
You should try but it's more math from here (you'll probably want something like ord c - ord 'A' for letters and 26 + ord c - ord '0' for digits to get it in the range 0-35 first.
Modification may well be just an addition of 3 to the Char ascii value.
I have gone through several books and can't find a solution off the shelf.
(Returning the Char list can be to a different list variable.)
import Data.Char
shiftAscii :: String -> String
shiftAscii xs = map (chr.(+3).ord) xs
would do what you ask.
It works because map edits each character in the string using the supplied function.
ord converts the Char to its Int value
(+3) shifts the (ascii) by 3
chr converts back to a Char,
so chr.(+3).ord is those three strung together with function composition .
To be more flexible, you could write
shiftAsciiBy :: Int -> String -> String
shiftAsciiBy n = map (chr.(+ n).ord)
notice that shifting the ascii doesn't respect alphabet boundaries, so if you were needing this to do rot13 encoding or similar simple shift, you'd be better off with a hand-rolled shift function that only edits the alphabet
addAscii :: Int -> Char -> Char
addAscii n c | isUpper c = chr $ ((ord c - ord 'A' + n) `mod` 26) + ord 'A'
| isLower c = chr $ ((ord c - ord 'a' + n) `mod` 26) + ord 'a'
| otherwise = c
for example
['A'..'z']
"ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz"
and we shift just the alphabet ascii:
map (addAscii 5) ['A'..'z']
"FGHIJKLMNOPQRSTUVWXYZABCDE[\\]^_`fghijklmnopqrstuvwxyzabcde"