kakann
kakann

Reputation: 1

haskell translate string to Int

I'm trying to make a function that translate for example the string B1 C2 into two int tuples using the findLetter function defined below. B = 2 and C = 3. It will also reverse the tuple order. Hence B1 C5 == ((1,2),(5,3)).

accoList = [("A", "1"),("B", "2"),("C", "3"),("D", "4"),("E", "5"),
("F", "6"),("G", "7"),("H", "8")]

findLetter :: String -> String
findLetter c = findLetter' c accoList
where findLetter' c ((x,y):xs) = if x == c then y else findLetter' c xs
    findLetter' c [] = undefined

--userToMove1 :: String -> ((Int, Int),(Int, Int))
userToMove1 s = userToMove' $ take 5 s
   where userToMove' (a:b:c:d:e:xs) =  ((read b , (read (findLetter a))),((read e), (read (findLetter d))))

When I run this code I get exceptions like non-exhaustive pattern and when I don't comment out --userToMove1 :: String -> ((Int, Int),(Int, Int)), the program gets full of errors. I don't know how to fix it :(

Upvotes: 0

Views: 209

Answers (3)

Niko
Niko

Reputation: 401

In ghci you can use :t userToMove1 to inspect the inferred type of your function. In your case this returns:

*Main> :t userToMove1
userToMove1
  :: (Read t, Read t1, Read t2, Read t3) =>
     [String] -> ((t3, t2), (t1, t))

ghci inferred that your input parameter is a List of Strings instead of just a String, like you want it to be.

Why does this happen?

In userToMove1 you pattern match on a List and feed the elements of the List to your findLetter function. Those expect Strings, therefore the matched elements (a,b,c,d,e) must be Strings, therefore s must be a List of Strings. If you rewrite accoList and findLetter to accept Chars instead, ghci would infer s to be a List of Chars which is a String. Then your original typsignature would work.

Assuming this is for a chess game, using Chars instead of Strings would be preferrable anyway, as positions can only be single letters.

Upvotes: 0

Bob Dalgleish
Bob Dalgleish

Reputation: 8227

Your pattern match (a:b:c:d:e:xs) takes the first 5 characters of the string; each is of type Char and not of type String, which is a synonym for [Char]. Because the type doesn't match, it can't find appropriate functions for read or findLetter.

You can fix the immediate problem by converting the characters back to strings, thus:

where userToMove' (a:b:c:d:e:xs) =  ((read [b] , (read (findLetter [a]))),((read [e]), (read (findLetter [d]))))

Also, you could redefine findLetter as:

findLetter c = fromJust $ lookup c accoList

Upvotes: 1

Lorenzo
Lorenzo

Reputation: 2210

The problem is that you confuse Chars with Strings.

When you pattern-match a String to (a:b:c:d:e:xs), all the elements except for xs are actually Chars and not String -- that's why xs has an 's' at the end, remember Strings are lists.

So the findLetter should have signature findLetter :: Char -> String as it takes a Char from userToMove1 and returns a String from accoList

Also it doesn't really make sense for findLetter to return a String, you can either read the string in findLetter itself or even better have an Int in the tuple. This is how I would write this:

accoList = [('A', 1),('B', 2),('C', 3),('D', 4),('E', 5),('F', 6),('G', 7),('H', 8)]

findLetter :: Char -> Int
findLetter c = findLetter' c accoList
  where findLetter' c ((x,y):xs)
    | x == c    = y
    | otherwise = findLetter' c xs


userToMove1 :: String -> ((Int, Int),(Int, Int))
userToMove1 s = userToMove' $ take 5 s
  where userToMove' (a:b:c:d:e:[]) =  ((read b , findLetter a),(read e, findLetter d))

Finally, I'd like to say that this code looks weird, and there's probably a better way of achieving whatever you want to achieve

Upvotes: 1

Related Questions