[英]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. 它将
Char
和Int
转换为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
转换为Int
或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 这个想法是,您输入类似: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代码中,
head
, tail
是不必要的,应将其视为代码气味。
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.