Reputation: 1
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
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
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
Reputation: 2210
The problem is that you confuse Char
s with String
s.
When you pattern-match a String
to (a:b:c:d:e:xs)
, all the elements except for xs
are actually Char
s 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