简体   繁体   中英

Haskell Custom Data Type

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM