简体   繁体   English

Haskell自定义数据类型

[英]Haskell Custom Data Type

I am working on a homework assignment and we have an array of mixed elements Char and Int. 我正在做作业,我们有一系列混合元素Char和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. 它将CharInt转换为Element所以我可以处理它们。

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 . 因此,现在我做了(头数组)并返回Element该如何将Element转换为IntChar

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 这个想法是,您输入类似:postfix_eval [1,2,3,'+',4,'+','*']的结果,并且您会以Int的形式得到答案,在这种情况下,您得到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. 特别是,在大多数惯用的Haskell代码中, headtail是不必要的,应将其视为代码气味。

Further, avoid if /guards that can be replaced by pattern matching. 此外,避免if /警卫可通过图案匹配来代替。 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函数的第一次重做:

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? 首先,为什么错误返回0 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? 其次,函数extractInt必定是局部的(或再次返回假结果):如果a代替char怎么办?

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. 这种类型的确存在:我们并非总是对所有输入都有结果Element 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. 瞧,不需要危险的extract功能。 When the inputs are not integers, we simply return Nothing since the first matches fail. 当输入不是整数时,由于第一个匹配失败,我们只返回Nothing

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. 这可能会觉得不太方便,因为现在我们需要在代码的其余部分中处理Maybe Element ,而该代码不再收到要使用的简单Element 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. Maybe Element类型迫使我们这样做。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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