Reputation: 1119
I am working on a homework assignment and we have an array of mixed elements Char and Int.
I created this custom type:
data Element = CharToElement Char | IntToElement Int
Which converts Char
s and Int
s into Element
s so I can deal with them.
The issue I am having is what to do when I want to convert back?
So now that I do (head array) and that returns Element
how do I convert that Element
to an Int
or Char
.
This is for a postfix evaluator program.
Help is appreciated. Thanks.
Edit:
So I worked on it and this is what I have:
data Element = ElementConstructor (Char, Int, Bool) | IntToElement Int | CharToElement Char | NIL
extractInt (_,i,_) = i
extractChar (c,_,_) = c
extractType (_,_,b) = b
stack_push :: [Element] -> Element -> [Element]
stack_push stack element = (element:stack)
stack_pop :: [Element] -> [Element]
stack_pop stack = (tail stack)
stack_top :: [Element] -> Element
stack_top stack = (head stack)
postfix_eval eqn = (postfix_eval_rec (postfix_eval_const eqn []) [])
postfix_eval_const eqn_raw eqn_conv =
if null eqn_raw
then NIL
else
if (isNumber (head eqn_raw))
then ElementConstructor(NIL, (head eqn_raw), True):eqn_conv
else if (isAlpha (head eqn_raw))
then ElementConstructor((head eqn_raw), NIL, False):eqn_conv
postfix_eval_const (tail eqn_raw) eqn_conv
operate :: Element -> Element -> Element -> Element
operate operator_char a b =
if operator_char == '+'
then IntToElement ( (extractInt a) + (extractInt b) )
else if operator_char == '-'
then IntToElement ( (extractInt b) - (extractInt a) )
else if operator_char == '*'
then IntToElement ( (extractInt a) * (extractInt b) )
else if operator_char == '/'
then IntToElement ( (extractInt b) `div` (extractInt a) )
else
IntToElement(0)
postfix_eval_rec :: [Element] -> [Element] -> Int
postfix_eval_rec eqn stack =
if null eqn
then (extractInt (stack_top stack))
else if ( (extractType (head eqn)) )
then (postfix_eval_rec (tail eqn) (stack_push stack (head eqn) ) )
else
(postfix_eval_rec (tail stack) (stack_push (stack_pop (stack_pop stack)) (operate (head eqn) (stack_top stack) (stack_top (stack_pop stack)))))
The idea is that you enter something like: postfix_eval [1,2,3,'+',4,'+','*'] and you get an answer in the form of an Int, in this case you get 9
Upvotes: 2
Views: 1330
Reputation: 116139
There are many ways to fix this. I'll only provide some general suggestions.
First of all, strive to use only total functions -- functions that never error out. In particular, head
, tail
are unnecessary in most idiomatic Haskell code, and should be regarded as a code smell.
Further, avoid if
/guards that can be replaced by pattern matching. You really should get used to that, and there are good tutorials around.
For instance, here's a first reworking of your operate
function:
operate :: Element -> Element -> Element -> Element
operate (CharToElement '+') a b =
IntToElement (extractInt a + extractInt b)
operate (CharToElement '-') a b =
IntToElement (extractInt a - extractInt b)
operate (CharToElement '*') a b =
IntToElement (extractInt a * extractInt b)
operate (CharToElement '/') a b =
IntToElement (extractInt a `div` extractInt b)
operate _ _ _ =
IntToElement 0
This is far from being perfect. First, why returning 0
on error? There's no way the caller can distinguish that from an actual null result. Second, the function extractInt
will necessarily be partial (or again return a bogus result): what if a
is a char instead?
Ultimately, the big lie is already found at the type level:
operate :: Element -> Element -> Element -> Element
This type indeed lies: we do not always have a resulting Element
for all inputs. We want to model errors instead. This is more honest:
operate :: Element -> Element -> Element -> Maybe Element
and can be implemented, again, by pattern matching:
operate :: Element -> Element -> Element -> Maybe Element
operate (CharToElement '+') (IntToElement a) (IntToElement b) =
Just $ IntToElement $ a + b
operate (CharToElement '-') (IntToElement a) (IntToElement b) =
Just $ IntToElement $ a - b
operate (CharToElement '*') (IntToElement a) (IntToElement b) =
Just $ IntToElement $ a * b
operate (CharToElement '/') (IntToElement a) (IntToElement b) =
Just $ IntToElement $ a `div` b
operate _ _ _ =
Nothing
And voila, no need for dangerous extract
functions. When the inputs are not integers, we simply return Nothing
since the first matches fail.
This might feel less convenient, since now we need to handle the Maybe Element
in the rest of the code, which no longer receives a simple Element
to work with. But this is the whole point! The rest of the code needs to cope with the possible errors, so the error handling must be performed there as well. The Maybe Element
type forces us to do that.
Upvotes: 4