Reputation: 85
I have this function that must check if a character from a string is a letter, a digit or a punctuation sign, case when it returns True otherwise False. But if I test it, it doesn't work.
punct :: Char -> Bool
punct c | c `elem` ['.', ',', '?', '!', ':', ';', '(', ')'] = True
| c isDigit = True
| c isAlpha = True
| otherwise = False
This function adds a space after one of those above.
format :: String -> String
format s = intercalate "" [if (punct c) then (c:" ") else [c] | c <- s]
After That I need to delete those above, checked at punct. Actually I think that I can replace those from punct directly with space I've tried something like this:
format :: String -> String
format s = map (\c -> if punct c then (c:" ") else [c] )
But it's not working, any suggestions? I think i can use map and filter? Am I wrong?
Upvotes: 2
Views: 236
Reputation: 23548
Well, this code won't compile. Let's tackle why it won't compile one issue at a time:
First off, in your punct
function, this compilation error happens twice:
Foo.hs:7:11: error:
• Couldn't match expected type ‘(Char -> Bool) -> Bool’
with actual type ‘Char’
• The function ‘c’ is applied to one argument,
but its type ‘Char’ has none
In the expression: c isDigit
In a stmt of a pattern guard for
an equation for ‘punct’:
c isDigit
What the compiler is trying to tell you here is that you wrote "call c
as a function, passing it the argument isDigit
", when obviously you meant that the other way around. So first we swap those:
punct :: Char -> Bool
punct c | c `elem` ['.', ',', '?', '!', ':', ';', '(', ')'] = True
| isDigit c = True
| isAlpha c = True
| otherwise = False
With that fixed, your first format
function in fact compiles:
format1 :: String -> String
format1 s = intercalate "" [if (punct c) then (c:" ") else [c] | c <- s]
And it also seems to work as advertised:
*Foo> format1 "a.b_c"
"a . b _c "
Okay, now what about that second format
function? Well, at first it won't compile:
Foo.hs:18:12: error:
• Couldn't match type ‘[Char] -> [[Char]]’ with ‘[Char]’
Expected type: String
Actual type: [Char] -> [[Char]]
• Probable cause: ‘map’ is applied to too few arguments
In the expression: map (\ c -> if punct c then (c : " ") else [c])
In an equation for ‘format’:
format s = map (\ c -> if punct c then (c : " ") else [c])
The compiler is telling you here (with the "Probable cause" message) that you forgot to use s
on the right hand side, and should have written:
format s = map (\c -> if punct c then (c:" ") else [c] ) s
But event that won't compile: (but the error message is different now)
Foo.hs:18:12: error:
• Couldn't match type ‘[Char]’ with ‘Char’
Expected type: String
Actual type: [[Char]]
• In the expression:
map (\ c -> if punct c then (c : " ") else [c]) s
In an equation for ‘format’:
format s = map (\ c -> if punct c then (c : " ") else [c]) s
That's saying that your result should have been a String
(that is, a list of Char
, or [Char]
), but instead was a list of String
s (that is, a [[Char]]
)
This is because you turned each character into a string, instead of into a character. One way to fix this is to turn each character into a character:
format s = map (\c -> if punct c then ' ' else c ) s
Another way is to apply intercalate ""
(or concat
) as you did before to join the list of strings into a single string:
format s = intercalate "" $ map (\c -> if punct c then " " else [c] ) s
Upvotes: 1
Reputation: 476557
The reason why it is not working is because your map
maps Char
s on String
s (so [Char]
s). As a result the output of the map
would be [String]
. Nevertheless if you want to delete those above, your function in the map does not throw the chars away.
Furthermore you punct
function is wrong. isDigit
, etc. are functions. So you call it with isDigit c
, not c isDigit
:
punct :: Char -> Bool
punct c | c `elem` ['.', ',', '?', '!', ':', ';', '(', ')'] = True
| isDigit c = True
| isAlpha c = True
| otherwise = False
Nevertheless, you can easily make this statement more compact and transform it into:
punct :: Char -> Bool
punct c = isDigit c || isDigit c || c `elem` ['.', ',', '?', '!', ':', ';', '(', ')']
There are two options here: (1) either you do a post processing where you filter out all the punctuations; or (2) in case you delete the punctuations, you actually do a replacement instead of inserting a space.
(1) Post processing:
The post-processing is simply a filter
:
post_format :: String -> String
post_format = filter (not . punct)
You better use post processing, if you still want to use the "original" format
function.
(1) Replacement:
In that case you could use a helper
function:
helper :: Char -> Char
helper x | punct x = ' '
| otherwise = x
Then format
collapses to:
format :: String -> String
format = map helper
Upvotes: 2