2

I'm really an absolute newbie at Haskell, so I'm at a total loss as to how to debug some functions I wrote. When I call shuntingYard ["3+4"] I get back [], whereas I want to get back [34+]. Any and all help would be greatly, greatly appreciated.

import Char

isOperator :: Char -> Bool
isOperator x = elem x ['+','-','*','/','%','^','!','=','<','>']

associativityOf :: Char -> String
associativityOf x = if elem x ['+','-','*','/','%']
                    then "Left"
                    else "Right"

precedenceOf :: Char -> Int
precedenceOf x
    | elem x "=<>"   = 1 
    | elem x "+-"    = 2
    | elem x "*/%"   = 3
    | elem x "^!"    = 4
    | otherwise      = 0

operatorActions :: [[Char]] -> [[Char]] -> [[Char]]
operatorActions stmt stack
    | ( tokenAssoc == "Left"  && tokenPrecedence <= stackPrecedence ) ||        
      ( tokenAssoc == "Right" && tokenPrecedence <  stackPrecedence ) =
        [stackOper] : _shuntingYard stmt (tail stack)
    | otherwise   = _shuntingYard (tail stmt) ((head stmt) : stack)
    where tokenAssoc       = associativityOf (head (head stmt))
          tokenPrecedence  = precedenceOf    (head (head stmt))
          stackOper        = if (not (null stack))
                           then (head (head stack))
                           else '='
          stackPrecedence  = precedenceOf stackOper

stackOperations :: [[Char]] -> [[Char]]                                
stackOperations stack
    | ((not (null stack)) && (head (head stack)) == '(') = 
      error "Unbalanced parens."
    | null stack = []
    | otherwise  = (head stack) : _shuntingYard [] (tail stack)

_shuntingYard :: [[Char]] -> [[Char]] -> [[Char]]
_shuntingYard stmt stack
    | null stmt          = stackOperations stack
    | all isDigit (head stmt) = (head stmt) : _shuntingYard (tail stmt) stack
    | isOperator  (head (head stmt)) = operatorActions stmt stack
    | (head (head stmt)) == '('=
      _shuntingYard (tail stmt) ((head stmt) : stack)
    | (head (head stmt)) == ')' = if (head (head stack)) == '('
                            then _shuntingYard (tail stmt) (tail stack)
                            else (head stack) : _shuntingYard stmt (tail stack)
    | otherwise = _shuntingYard (tail stmt) stack

shuntingYard :: [[Char]] -> [[Char]]
shuntingYard stmt = _shuntingYard stmt []
Andy
  • 3,132
  • 4
  • 36
  • 68
  • Still looking at it, just some comments, instead of writing ['+','-','*','/','%','^','!','=','<','>'] you might find it easier to type "+-*/%^!=<>". Actually you did it in precedenceOf... – Ptival Mar 20 '11 at 19:16
  • Also, instead of using a string like "Left" or "Right" for associativity, you should consider defining a data type: data Assoc = Left | Right. This way you can easily pattern match, and be safe that you won't write "Rihgt" (because Rihgt won't type check). – Ptival Mar 20 '11 at 19:18
  • Pitval: How exactly would I modify my code to use this new data type? – Andy Mar 20 '11 at 19:23
  • 1
    Andrew: you'd define data Assoc = Left | Right, then associativityOf would be of type Char -> Assoc, and return those data constructors instead of the strings (Left instead of "Left", Right instead of "Right"). And the same modification in your guards in operatorActions. In this case the modification seems a little bit useless, but it allows you to pattern match easily for Left/Right if you have a function that takes an Assoc as input, and it will also save you from typos, because "Rihgt" would be a String, but Rihgt would not be an Assoc! – Ptival Mar 20 '11 at 19:37
  • Ptival: I get an error doing so, namely `Ambiguous occurence 'Left'` `It could refer to either 'Main.Left' [...] or 'Prelude.Left'` – Andy Mar 20 '11 at 19:53
  • 1
    ok, Left is already a data constructor of Either. Try to rename it, for instance data Assoc = AssocL | AssocR. The other solution is to type "Main.Left" and "Main.Right", but it's quite verbose, and you'll have to change it to the module name if you create a module... Plus, Left and Right will confuse Haskell people ; AssocL and AssocR won't (maybe Parsec users...). – Ptival Mar 20 '11 at 20:15
  • You might be interested to look at my implementation of shunting-yard in haskell: https://gist.github.com/913023 – Austin Taylor Apr 11 '11 at 15:26

2 Answers2

5

As a general debugging technique, you can use the Debug.Trace module to find out which functions are being called and what their inputs are. Using look at the state of your algorithm after each step.

import Debug.Trace

-- Show arguments each time _shuntingYard is called
_shuntingYard :: [[Char]] -> [[Char]] -> [[Char]]
_shuntingYard stmt stack = traceShow (stmt, stack) $ __shuntingYard stmt stack

__shuntingYard stmt stack
  | null stmt = stackOperations stack
  {- etcetera -}

This prints:

(["3+4"],[])
([],[])

Hmm, you lost everything after the first call. Looking at the guards in __shuntingYard, it seems that the "otherwise" case gets called.

Maybe you wanted to call shuntingYard ["3", "+", "4"]?

Heatsink
  • 7,721
  • 1
  • 25
  • 36
  • Oh, hey, that works. How can I modify this so that it takes a string as input rather than a list of strings? – Andy Mar 20 '11 at 19:18
  • 1
    @Andrew Write a lexical analyzer function that preprocesses the string, then pass the output into shuntingYard. Check out http://www.cse.chalmers.se/edu/course/TIN321/lectures/proglang-04.html for an introduction. In particular, look at the section titled "Implementing lexers (don't do like this!)". For your simple language, it's okay to do it like that. – Heatsink Mar 20 '11 at 19:36
3

Ok, let's just play through what happens when you call shuntingYard ["3+4"]:

  1. It calls _shuntingYard ["3+4"] []
  2. It goes through the guards of _shuntingYard:
    1. null stmt = null ["3+4"] = false
    2. all isDigit (head stmt) = all isDigit "3+4" = false as + is not a digit
    3. isOperator (head (head stmt)) = isOperator '3' = false
    4. Also false as '3' /= '('
    5. Also false as '3' /= ')'
  3. Since none of the guards matched, so we go into the default case and call _shuntingYard (tail stmt) stack = _shuntingYard [] []
  4. This time the first guard(null stmt = null []) matches, so we call stackOperations [] and get [].
sepp2k
  • 363,768
  • 54
  • 674
  • 675