Search code examples
haskellguard

how can i use more guards with small tricks?


When i compile my code in ghci, there is no problem. It can compile correctly. However, if i try to compile it in hugs, I get the error "compiled code too complex". I think the problem is due to many | conditions.

If I change it to use if/else, there is no problem. I can add if/else statements 100 times but this will be very tiresome and annoying. Rather than that, I tried to put if/else statements after 20-30 | conditions, but i cannot make | work inside if statements like the below:

f x y z     
    | cond1 = e1
    | cond2 = e2
    ...
    if (1)
    then
    | cond30 = e30
    | cond31 = e31
    ...
    else
    | cond61 = e61
    | cond62 = e62

How can I fix the code with the least effort? The complete code is on hpaste because it is longer than StackOverflow's question size limit.


Solution

  • Avoiding repetitive guards

    Firstly, you can rewrite

    function input 
       | this && that && third thing && something else = ...   -- you only actually needed brackets for (head xs) 
       | this && that && third thing && something different = ....
       | this && that && a change && ...  
    ...
       | notthis && ....
    

    with

    function input | this = function2 input'
                   | notthis = function4 input'
    
    function2 input | that = function3 input''
                    | notthat = ...
    

    That should simplify your 200 lines of copo code down, but it's still the wrong approach.

    Use a function to deal with the same problem just once, not every time

    The 4 cases for dealing with operations that you deal with time after time could be replaced with one function, perhaps like:

    operation :: Num a => Char -> a -> a -> a
    operation x = case x of
       '+' -> (+)
       '-' -> (-)
       '*' -> (*)
       '/' -> (/)
       _   -> error ("operation: expected an operation (+-*/) but got " ++ [c])
    

    Use list functions instead of testing characters one at a time

    You should use some standard functions to help reduce all the single character checks into just grabbing as much number as is there. takeWhile :: (a -> Bool) -> [a] -> [a], so

    takeWhile isDigit "354*243" = "354"
    takeWhile isDigit "+245" = ""
    

    and there's the corresponding dropWhile:

    dropWhile isDigit "354*1111" = "*1111"
    dropWhile isDigit "*1111" = "*1111"
    

    So the most dramatic shortening of your code would be to start copo with

    copo xs = let 
      numText = takeWhile isDigit xs
      theRest = droWhile isDigit xs 
      num = read numText
      ....
        in answer....
    

    but there's a shortcut if you want both takeWhile and dropWhile, called span, because span p xs == (takeWhile p xs, dropWhile p xs)

    copo xs = let 
      (numText,theRest) = span isDigit xs
      num = read numText
      ....
        in answer....
    

    Use recursion instead of repeating code

    You deal with 234 then 234*56 then 234*56/23 then ....

    You could replace this with a recursive call to copo, or produce a tree. This depends on whether you're supposed to obey the normal operator precedence (* or / before + or -) or not.