3

today I read a lot about Haskell but this formating is driving me crazy. I want to understand my basic errors as soon as possible so I can start coding normally. The function here should return a string that starts with the next "math Sign" example string (2sdwds+asd)+3 should return +asd)+3. Here is the code

getToNextSign :: String -> String 
getToNextSign str = do

let mathSigns = ['+' , '-' , '*' , '/' , '^' , ')']
let a = head str
if a `elem` mathSigns 
 then str
 else if tail str /= [] 
       then getToNextSign $ tail str
       else []

main = do
putStrLn $ getToNextSign "(2sdwds+asd)+3"

It gives me " parse error on input = ". I am also not sure about how exactly to call it in the main and do I really need the putStrLn function. I don't think I need it but I tried like 2874 different ways to write this and now I just gave up and need help.

3
  • 1
    Is the code you posted here exactly as it is indented in your source file? Seems like a syntax error from indentation. Commented Feb 4, 2014 at 21:09
  • 2 small things not related to your bug: you shouldnt be using do-notation if you are not usign monads and I would prefer to use a function from the stdlib to do this task (break / span / etc). Commented Feb 4, 2014 at 21:11
  • Yes the code is 1 to 1. I know it is probably from identation but I can't find it. Also @missingno I think I get what do you mean but as I said I tried many different ways and couldn't understand where the error is I think I didn't use "do" at my first implemetation. Commented Feb 4, 2014 at 21:26

3 Answers 3

3

Besides the improvement to formatting that Stephen Diehl provided, there are other improvements you can make. As Carl points out, you can replace the if-else if-else with guards, like so:

getToNextSign :: String -> String
getToNextSign str
  | a `elem` mathSigns = str
  | tail str /= []     = getToNextSign $ tail str
  | otherwise          = []
  where
    a = head str
    mathSigns = ['+' , '-' , '*' , '/' , '^' , ')']

When you're at it, you can also replace the head/tail with pattern matching, as in

getToNextSign :: String -> String
getToNextSign (c:cs)
  | c `elem` mathSigns = c:cs
  | not (null cs)      = getToNextSign cs
  | otherwise          = []
  where
    mathSigns = ['+' , '-' , '*' , '/' , '^' , ')']

and if you're gonna do pattern matching, you can just as well take it all the way.

getToNextSign :: String -> String
getToNextSign str = case str of
     c:_ | c `elem` mathSigns -> str
     c:[] -> []
     _:cs -> getToNextSign cs
  where mathSigns = ['+' , '-' , '*' , '/' , '^' , ')']

and when you do it like this, you realise that you haven't really handled the case when getToNextSign gets an empty list as an argument, which is perhaps something you want to do.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you very much. I didn't handle the case when it gets an empty list because I check before I call it if the list is empty. I will post another question soon because my next function is using guards and I hope I will do them correctly but I guess I will need help also.
This is good. We could further refactor with getToNextSign [] = [] and then getToNextSign str@(c:cs) | c `elem` mathSigns = str | otherwise -> getToNextSign cs.
@enoughreptocomment I thought about that but decided to not include it because it would introduce yet another (possibly) foreign concept – the @ alias in patterns.
3

Here is a simpler alternative to your problem using the Prelude list functions, dropWhile and elem, which has a type sig (a -> Bool) -> [a] -> [a]. It takes a function which will drop the elements from a list as long as the condition provided by the function is true.

Hence, your function can be rewritten as follows.

let getNextSign x = dropWhile  ( not . `elem` "+-*/^)" ) x

Try to avoid explicit recursion when possible and put them higher order functions to good use. And the Prelude has tons of list manipulation functions which come in handy all the time.

Comments

2

Haskell, being whitespace sensitive, has to have the body of the function indented beyond the toplevel. An direct fix for your original code would be:

getToNextSign :: String -> String
getToNextSign str = do
  let mathSigns = ['+' , '-' , '*' , '/' , '^' , ')']
  let a = head str
  if a `elem` mathSigns
   then str
   else if tail str /= []
         then getToNextSign $ tail str
         else []

main = do
  putStrLn $ getToNextSign "(2sdwds+asd)+3"

As pointed out in the comments you don't need do notation here since you aren't using a monad. The let statements can be instead be written as where statements.

getToNextSign :: String -> String
getToNextSign str =
  if a `elem` mathSigns
   then str
   else if tail str /= []
         then getToNextSign $ tail str
         else []
  where
    a = head str
    mathSigns = ['+' , '-' , '*' , '/' , '^' , ')']

5 Comments

Really thank you. I guess I would have to check every space. But is a whole blank line a problem because that seems to be the only difference.
@user3129475 No, that's not even close to the only difference. The important difference is that the body of the function must be indented. You didn't indent the if, but it's mandatory to do so.
@StephenDiehl I'd strongly recommend a third version using guards instead of nested ifs. This is exactly why they're in the language.
@Carl can you show it with guards I have an idea how to do it but can't really.
@user3129475 It's too long for a comment, so I have put it down in my answer.

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.