2

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?

4
  • What do you mean with "delete those above"? The punctuations? Commented May 11, 2017 at 12:38
  • @Willem Van Onsem: The signs that return true in punct, yes Commented May 11, 2017 at 12:41
  • 1
    When you say "it's not working", please specify what you mean. Since this code won't compile, you should say "It doesn't compile", and give the error message. Commented May 11, 2017 at 12:43
  • What do you mean by c isDigit ? Commented May 11, 2017 at 12:55

2 Answers 2

2

The reason why it is not working is because your map maps Chars on Strings (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
Sign up to request clarification or add additional context in comments.

1 Comment

thank you a lot. I didn't realise that I defined wrog punct because I don't know the synthax very well. Thank a lot again
1

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 Strings (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

1 Comment

Thank you for the explanation, I've started to understand :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.