用户定义数据类型的操作 [英] Operations with user defined Datatype
问题描述
我有一个数据类型
data Expr = ExprNum Double - 常量
| ExprVar字符串 - 变量
| ExprAdd Expr Expr
| ExprSub Expr Expr
| ExprNeg Expr - 一元' - '操作符
| ExprMul Expr Expr
| ExprDiv Expr Expr
导出显示
如果我有(3 * 4 + 5)
数据类型 Expr
它应该返回 17
,if表达式包含一个变量:(3 * x)
它应该返回 ExprMul(ExprNum 3)(ExprVarx)
我试过这样做:
calculate(ExprMul ab)= a * b
计算(ExprAdd ab)= a + b
计算(ExprDiv ab)= a`div` b
计算(ExprSub ab)= a - b
计算(ExprVar a)= a
计算(ExprNum a)=读取a:Double
但我无法执行它。
我在这里做错了什么?
我想知道的另一件事是,如果用户输入Expr类型的值,并且需要将其转换为字符串格式,即如果用户输入为
$ $ $ $ $ $ ExprAdd(ExprNum 1)(ExprVarx)
我希望得到如下输出:
<$ p $
我试过使用
Read
请任何人都可以帮忙。谢谢。
看看你的问题和你的例子,我认为我们可以用一些很好的方式来使用一些类型来获得一些语法糖。
你可以在最后找到完整的代码,但是由于Luke对我的一个很好的解释,我没有解释这些东西,所以我们就开始做这件事。
首先我们必须在执行 calculate
时小心处理一些小问题,然后我们可以开始查找g为一些语法糖来支持输入,如(3 * 4 + 5):: Expr
。 执行 calculate
这对我来说并不是100%清楚,但我认为您希望您的 calculate
来签名:
calculate :: Expr - > Expr
大多数情况下,您会看到诸如 evaluate :: Expr - >也许Double
但我认为在这种情况下(因为你想要 3 * x
是 ExprMul(ExprNum 3)(ExprVar x)
)我们只是想简化使用 calculate
。
所以让我们试着简化一下。
对于形式为 ExprNum
和 ExprVar
我们可以不做任何事情来简化事情,所以让我们诚实地说出来:
计算一个@(ExprNum _)= a
计算一个@(ExprVar _)= a
如果你没有看到 a @(ExprNum _)
语法,它只是匹配(ExprNum _)
但要记住 a
中的compelte匹配表达式。
考虑到这一点,我们必须关心更多有趣的情况 - 例如乘法:
calculate(ExprMul ab)= a * b
问题在于您的右侧 a * b
现在,这不能工作,因为(*)
需要它的操作数在 Num
和 Expr
尚未(至今)。
当然,我们真正想要的是如果可以的话计算产品 - 意味着如果 a
和 b
确实是数字 - 或者在这种情况下是 ExprNum
。
最简单的方法来检查这个(我可以想到)是递归简化两个操作符 a
和 b
,然后使用 case
表达式:
calculate(ExprMul ab)= let a'=计算a
b'=计算b
',b')
(ExprNum a'',ExprNum b'') - > ExprNum(a''* b'')
_ - >我们来看看这一步:
- 首先将
a
和b
简化为a'
和b'
使用计算
recursivley - 然后,如果这两个都是数字(
ExprNum
),那么它会将它们的值的乘积作为另一个ExprNum
如果它们不是
ExprNum
它只是产生 ExprMul
使用简化的术语最后一点是魔法发生的地方 - 因为我们得到了算法将尝试的简化术语简化子类,即使变量在附近,我们也无法得到完整的评估。
重构一下
现在我们可以对 ExprDiv
, ExprAdd
等其他表达式执行相同的操作,但我并不喜欢重复一遍又一遍的东西,让我们重构一下:
operateOnNums ::(Expr - > Expr - > Expr)
- > (Double - > Double - > Double)
- > Expr - > Expr - > (a',b')为
(ExprNum a'',ExprNum b)的情况下计算b $ b b'=计算b
defpr = b $ b operationOnNums def fab = '') - > ExprNum(f a''b'')
_ - > def a'b'
operateOnNum ::(Expr - > Expr)
- > (Double - > Double)
- > Expr - > Expr
operateOnNum def f a = let a'=在
的前提下计算
ExprNum a'' - > ExprNum(f a'')
_ - > def a'
即使这些看起来有点儿差不多,我们也是如此更复杂。
我们再次检查是否可以简化子项(两个用于 operateOnNums
,另一个用于 operateOnNum
) ExprNum
表达式,如果是这样,则将这些值的
值应用于函数 f
(这将是操作在实数上 - 例如(*)
用于 ExprMul
或否定$ c $如果没有使用
def (意思是* defaults *)将简化的子程序包装到
ExprNeg中,现在计算自己看起来相当不错(或者我认为):
code> calculate :: Expr - > Expr
计算(ExprMul ab)= operateOnNums ExprMul(*)ab
计算(ExprAdd ab)= operateOnNums ExprAdd(+)ab
计算(ExprDiv ab)= operateOnNums ExprDiv(/)ab
计算(ExprSub ab)= operateOnNums ExprSub( - )ab
计算(ExprNeg a)= operateOnNum ExprNeg否定a
计算a = a
,我想我们可以继续。
启用语法糖:实现一些类型类 h2>
Num
Num
这给我们留下了一个问题,希望能够输入 3 * 4 + 5 :: Expr
之类的东西。
现在有一个基本的类型类名为 Num
,它为我们提供了完全相同的方法。
你基本上必须告诉Haskell如何做一个足够大的子集,像 *
, +
,..以及一个函数 fromIntegral
,它将翻译数字,如 0
, 1
, 2
,... into Expr
。
真正好的是,我们有那些运算符,甚至来自整数的 因此,让我们使 容易不是吗? 注意我欺骗了使用 这样做已经可以工作了: @behklir建议实现这个类型 - 使用this和 OverloadedString 扩展,我们将能够评估如下所示: 已经以我们的类型构造函数 em>
,而不是将它编译成 ExprMul
, ExprAdd
,..和 ExprNum
。
Expr
一个 Num
的实例:
实例Num Expr其中
a + b = ExprAdd ab
a * b = ExprMul ab
否定a = ExprNeg a
fromInteger n = ExprNum(fromInteger n)
abs _ = undefined
signum _ = undefined
abs
和 signum
函数。如果你喜欢,如果你为它们添加其他 Expr
个案并相应补充 calculate
函数,你也可以实现这些 - 但对于OP给出的例子,他们并不是真的需要在这里。
>计算$ 3 * 4 + 5
ExprNum 17.0
>计算$ 3 * 4 + ExprVara
ExprAdd(ExprNum 12.0)(ExprVara)
IsString
>计算$ 3 * 4 +a
ExprAdd(ExprNum 12.0)(ExprVara)
使用 fromString
从<$转换a
,OverloadedStrings [char]
,所以让我们实现 IsString
- 这又很简单,因为我们已经有 fromString
形式为 ExprVar
:
instance IsString Expr where
fromString = ExprVar
不要忘记用 这个是@ØrjanJohansen的一个建议,它可以让我们编写像 的东西,我认为你可以像这样工作: 这很像是小数值和分数的 就是这样 - 请确保指向我的东西不是100%清晰或拼写错误。 您可能会问您如何评估变量。 由于我们不再对结果表达式感兴趣,在它的值中,我们称之为 应该是直截了当的 - 唯一真正的新事物是 一个例子可能如下所示: 正如您所看到的,我只提供了一对,匹配 如果算法找不到匹配变量 { - #LANGUAGE OverloadedStrings# - } $ c $启用
确实(在这里)。 OverloadedStrings
来源。
在GHCi中,您可能想要
- 以开始它> ghci -XOverloadedStrings
- :set -XOverloadedStrings
GHCi内
$ b
小数
>计算$a/ 4.5
ExprDiv(ExprVara)(ExprNum 4.5)
Num
- 我想你知道现在发生了什么:
instance Fractional Expr其中
fromRational r = ExprNum(fromRational r)
a / b = ExprDiv ab
如何评估变量
evaluate
:
import Data.Maybe fromMaybe)
evaluate :: [(String,Double)] - > Expr - > Double
评估env(ExprMul ab)=评估env a *评估env b
评估env(ExprAdd ab)=评估env a +评估env b
评估env(ExprDiv ab)=评估env a评估env b
评估env(ExprSub ab)=评估env a - 评估env b
评估env(ExprNeg a)=否定评估env a
评估_(ExprNum n)= n
评估env(ExprVar v)= fromMaybe 0 $ lookup v env
env
:
我们需要知道变量的值。所以我们传入一个包含( variable , value )对的环境。然后,我们可以使用 lookup
以及 fromMaybe
来查找变量的值。
>评估[(a,5)](3 * 4 +a)
17.0
a
至 5
- 然后使用使用 ExprVara
(这里当然是隐藏使用 IsString
和 OverloadedStrings
)。
lookup
将会返回 Nothing
,这就是 fromMaybe
的作用:我决定将变量默认为 0
在这种情况下,这正是
如果你不想要默认的0行为
如果您不喜欢它,那么对于不在环境中的变量,这将返回 这当然会使用一些沉重的武器( 请理解我无法再写出另一大块文字来详细解释。 基本上那些只是因为我懒得对 参考和更容易复制和粘贴这里是完整的代码: 0
,您可以更改 evaluate <
$ b $ import Control.Applicative((< $>))
import Control.Monad(liftM2)
evaluate :: [(String,Double)] - > ; Expr - >也许Double
评估env(ExprMul ab)= liftM2(*)(评估env a)(评估env b)
评估env(ExprAdd ab)= liftM2(+)(评估env a)(评估env b)
评估env(ExprDiv ab)= liftM2(/)(评估env a)(评估env b)
评估env(ExprSub ab)= liftM2( - )(评估env a)(评估env b)
评估env(ExprNeg a)=否定< $>评估env a
evaluate _(ExprNum n)=只是n
评估env(ExprVar v)= lookup v env
liftM2
brining (*)
)进入也许
monad和(< $>)
与取反相同
评估env a
和评估env b $的结果进行模式匹配c $ c>处理4个案例(
Nothing,Nothing
, Nothing,Just
,...) - I我只关心 Just,Just
无论如何,这些都是这样做的:在 Just case中执行操作并返回
Nothing
其他地方。
完整代码
$ $ b $
$ import $ Data.Maybe(fromMaybe )
import Data.String(IsString(..))
data Expr = ExprNum Double - 常量
| ExprVar字符串 - 变量
| ExprAdd Expr Expr
| ExprSub Expr Expr
| ExprNeg Expr - 一元' - '操作符
| ExprMul Expr Expr
| ExprDiv Expr Expr
派生显示
实例Num Expr其中
a + b = ExprAdd ab
a * b = ExprMul ab
否定a = ExprNeg a
fromInteger n = ExprNum(fromInteger n)
abs _ = undefined
signum _ = undefined
实例小数Expr其中
fromRational r = ExprNum(fromRational r)
a / b = ExprDiv ab
实例IsString Expr其中
fromString = ExprVar
评估:: [(String,Double)] - > Expr - > Double
评估env(ExprMul ab)=评估env a *评估env b
评估env(ExprAdd ab)=评估env a +评估env b
评估env(ExprDiv ab)=评估env a评估env b
评估env(ExprSub ab)=评估env a - 评估env b
评估env(ExprNeg a)=否定评估env a
评估_(ExprNum n)= n
评估env(ExprVar v)= fromMaybe 0 $ lookup v env
calculate :: Expr - > Expr
计算(ExprMul ab)= operateOnNums ExprMul(*)ab
计算(ExprAdd ab)= operateOnNums ExprAdd(+)ab
计算(ExprDiv ab)= operateOnNums ExprDiv(/)ab
计算(ExprSub ab)= operateOnNums ExprSub( - )ab
计算(ExprNeg a)= operateOnNum ExprNeg否定a
计算a = a
operateOnNums ::(Expr - > Expr - > Expr) - > (双 - >双 - >双) - > Expr - > Expr - > (a',b')为
(ExprNum a'',ExprNum b)的情况下计算b $ b b'=计算b
defpr = b $ b operationOnNums def fab = '') - > ExprNum(f a''b'')
_ - > def a'b'
operateOnNum ::(Expr - > Expr) - > (Double - > Double) - > Expr - > Expr
operateOnNum def f a = let a'=在
的前提下计算
ExprNum a'' - > ExprNum(f a'')
_ - > def a'
一些例子
>计算$ 3 * 4 + 5
ExprNum 17.0
>计算$ 3 * 4 +a
ExprAdd(ExprNum 12.0)(ExprVara)
>计算$ 3 *a+5
ExprAdd(ExprMul(ExprNum 3.0)(ExprVara))(ExprNum 5.0)
> (ExprMum(ExprNum 3.0)(ExprNum 4.0))(ExprVara)
>计算$a/ 4.5
ExprDiv(ExprVara)(ExprNum 4.5)
>评估[(a,5)](3 * 4 +a)
17.0
这是(我认为)你想从什么开始
备注:
不要忘记在GHCi中启用 OverloadedStrings
,如果你想试试这个:
- 用开始它> ghci -XOverloadedStrings
- :set -XOverloadedStrings
在GHCi中
I have a datatype
data Expr = ExprNum Double -- constants
| ExprVar String -- variables
| ExprAdd Expr Expr
| ExprSub Expr Expr
| ExprNeg Expr -- The unary '-' operator
| ExprMul Expr Expr
| ExprDiv Expr Expr
deriving Show
If I have (3* 4 + 5)
of datatype Expr
it should return 17
and if the expression includes a variable: (3 * x)
it should return ExprMul (ExprNum 3) (ExprVar "x")
I tried doing it this way:
calculate (ExprMul a b) = a * b
calculate (ExprAdd a b) = a + b
calculate (ExprDiv a b) = a `div` b
calculate (ExprSub a b) = a - b
calculate (ExprVar a )= a
calculate (ExprNum a ) = Read a : Double
But I am unable to execute it. What am I doing wrong here?
Another thing that I wish to know about is that if a user enters a value in Expr type ,and I need to convert it into string format,i.e,if User enters as
ExprAdd (ExprNum 1) (ExprVar "x")
I wish to get the output as follows
1+x
I tried using Read
but I am unable to execute it.
Please if anyone can help.Thankyou.
Looking at your question and your examples, I think we can make this happen in a nice way using some type-classes to get some syntactic sugar.
You can find the complete code at the end but as Luke made a good point on me not explaining stuff let's start doing this.
First we have to take care on some minor problems in your implementation of calculate
and then we can start looking for some syntactic sugar to enable input like (3* 4 + 5) :: Expr
.
implementing calculate
It was not 100% clear to me but I think you want your calculate
to have this signature:
calculate :: Expr -> Expr
I most cases you see things like evaluate :: Expr -> Maybe Double
but I think in this case (because you want 3 * x
to be ExprMul (ExprNum 3) (ExprVar "x")
) we want indeed just to simplify terms using calculate
.
So let's try to simplify things.
For expressions of the form ExprNum
and ExprVar
we can do nothing more to simplify things as it is, so let's be honest and say so:
calculate a@(ExprNum _) = a
calculate a@(ExprVar _) = a
If you did not see the a@(ExprNum _)
syntax before it's just fancy way of matching (ExprNum _)
but remembering the compllete matched expression in a
.
With this in mind we have to take care of the more interesting cases - for example multiplication:
calculate (ExprMul a b) = a * b
The problem is your right side a * b
Right now this cannot work as (*)
needs to have it's operands to be in Num
and Expr
is not (yet).
Of course what we really want is to calculate the product if we can - meaning if both a
and b
are indeed numbers - or in this case ExprNum
.
The easiest way to check this (I can come up with) is to recursivley simplify both operants a
and b
and then to use a case
expression:
calculate (ExprMul a b) = let a' = calculate a
b' = calculate b
in case (a',b') of
(ExprNum a'', ExprNum b'') -> ExprNum (a''*b'')
_ -> ExprMul a' b'
Let's look at this step by step:
- first it simplifies
a
andb
intoa'
andb'
usingcalculate
recursivley - then if both of these are numbers (
ExprNum
) it yield the product of their values as anotherExprNum
- if they are not both
ExprNum
s it just yield anExprMul
using the simplified term
The last point is where a bit of the magic happens - because we yield the simplified terms the algorithm will try to simplify subterms even if a variable is around and we cannot get a complete evaluation.
refactoring a bit
Now we can do the same to the other expressions like ExprDiv
, ExprAdd
, ... but I did not really like to repeat the stuff again and again so let's refactor this out:
operateOnNums :: (Expr -> Expr -> Expr)
-> (Double -> Double -> Double)
-> Expr -> Expr -> Expr
operateOnNums def f a b = let a' = calculate a
b' = calculate b
in case (a',b') of
(ExprNum a'', ExprNum b'') -> ExprNum (f a'' b'')
_ -> def a' b'
operateOnNum :: (Expr -> Expr)
-> (Double -> Double)
-> Expr -> Expr
operateOnNum def f a = let a' = calculate a
in case a' of
ExprNum a'' -> ExprNum (f a'')
_ -> def a'
This is really just the same we did above even if these might seem to be a bit more complicated.
Again we only check if we can simplify subterms (two for operateOnNums
and only one for operateOnNum
) into ExprNum
expressions and if so apply the
value of those to a function f
(that will be the operations on the real numbers - like (*)
for ExprMul
or negate
for ExprNeg) and if not use
def(meaning *defaults*) to wrap the simplified subterms into
Expr`s again.
Calculate itself now look rather nice (or so I think):
calculate :: Expr -> Expr
calculate (ExprMul a b) = operateOnNums ExprMul (*) a b
calculate (ExprAdd a b) = operateOnNums ExprAdd (+) a b
calculate (ExprDiv a b) = operateOnNums ExprDiv (/) a b
calculate (ExprSub a b) = operateOnNums ExprSub (-) a b
calculate (ExprNeg a) = operateOnNum ExprNeg negate a
calculate a = a
and I think we can move on.
enabling syntactic sugar: implementing some type-classes
Num
This leaves us with the problem that we want to be able to input something like 3*4+5 :: Expr
.
Now there is a basic type-class named Num
that provides us exactly with the means to do so.
You basically have to tell Haskell how to do a big enough subset of the basic math-operators like *
, +
, .. together with a function fromIntegral
that will translate numbers like 0
, 1
, 2
, ... into Expr
.
What's really nice here is, that we have those operators and even the fromIntegral
already handy in form of our type-constructors ExprMul
, ExprAdd
, .. and ExprNum
.
So let's make Expr
an instance of Num
:
instance Num Expr where
a + b = ExprAdd a b
a * b = ExprMul a b
negate a = ExprNeg a
fromInteger n = ExprNum (fromInteger n)
abs _ = undefined
signum _ = undefined
Easy isn't it?
Note that I cheated with the abs
and signum
functions. If you like you can implement these too if you add other Expr
cases for them and complement the calculate
function accordingly - but for the examples the OP gave they are not really needed here.
With this done this will already work:
> calculate $ 3*4+5
ExprNum 17.0
> calculate $ 3*4 + ExprVar "a"
ExprAdd (ExprNum 12.0) (ExprVar "a")
IsString
@behklir suggested to implement this type-class as well - using this and the OverloadedString extension we will be able to evaluate something like this:
> calculate $ 3*4+"a"
ExprAdd (ExprNum 12.0) (ExprVar "a")
OverloadedStrings is there to translate "a"
using the fromString
from IsString
instead of just compiling it into a [char]
, so let's implement IsString
- which again is really easy as we already have fromString
in the form of ExprVar
:
instance IsString Expr where
fromString = ExprVar
Don't forget to enable OverloadedStrings
with {-# LANGUAGE OverloadedStrings #-}
in source.
In GHCi you might want to
- start it with ghci -XOverloadedStrings
- :set -XOverloadedStrings
inside GHCi
Fractional
This one was a suggestion from @ØrjanJohansen to enable us to write things like
I think you can get this working something like this:
> calculate $ "a" / 4.5
ExprDiv (ExprVar "a") (ExprNum 4.5)
It's much like Num
for fractional values and the division - I think you know what comes by now:
instance Fractional Expr where
fromRational r = ExprNum (fromRational r)
a / b = ExprDiv a b
That's it - please make sure to point me to things not 100% clear or misspelled.
How to evaluate Variables
You might ask how you could evaluate Variables.
As we are no longer interested in a resulting expression but only in it's value let's call it evaluate
:
import Data.Maybe (fromMaybe)
evaluate :: [(String, Double)] -> Expr -> Double
evaluate env (ExprMul a b) = evaluate env a * evaluate env b
evaluate env (ExprAdd a b) = evaluate env a + evaluate env b
evaluate env (ExprDiv a b) = evaluate env a / evaluate env b
evaluate env (ExprSub a b) = evaluate env a - evaluate env b
evaluate env (ExprNeg a) = negate $ evaluate env a
evaluate _ (ExprNum n) = n
evaluate env (ExprVar v) = fromMaybe 0 $ lookup v env
Most of this should be straight forward - the only really new thing is env
:
We need to know what value a variable has. So we pass in an environment with (variable,value) pairs. We can then use lookup
together with fromMaybe
to find values for variables.
An example could look like this:
> evaluate [("a",5)] (3*4+"a")
17.0
As you can see I just provided a single pair, matching "a"
to 5
- and then use an expression using ExprVar "a"
(here of course hidden using IsString
and OverloadedStrings
).
In case the algorithm will not find a matching variable lookup
will return Nothing
and this is where fromMaybe
comes into play: I decided to default variables to 0
in this case and this is exactly what fromMaybe 0 :: Maybe Double -> Double
does (here).
if you don't want no default-to-0 behaviour
In case you don't like it that this will return 0
for variables not in the environment you can change evaluate
to be partial (or better: return Maybe Double
) like this:
import Control.Applicative((<$>))
import Control.Monad (liftM2)
evaluate :: [(String, Double)] -> Expr -> Maybe Double
evaluate env (ExprMul a b) = liftM2 (*) (evaluate env a) (evaluate env b)
evaluate env (ExprAdd a b) = liftM2 (+) (evaluate env a) (evaluate env b)
evaluate env (ExprDiv a b) = liftM2 (/) (evaluate env a) (evaluate env b)
evaluate env (ExprSub a b) = liftM2 (-) (evaluate env a) (evaluate env b)
evaluate env (ExprNeg a) = negate <$> evaluate env a
evaluate _ (ExprNum n) = Just n
evaluate env (ExprVar v) = lookup v env
This of course uses some heavy weaponary (liftM2
brining (*)
into the Maybe
monad and (<$>)
doing the same with negate
).
Please understand that I cannot write another big block of text to explain those in detail.
Basically those are just there because I am to lazy to pattern-match on the results of evaluate env a
and evaluate env b
to do handle 4 cases (Nothing, Nothing
, Nothing, Just
, ...) - I am only interested in Just,Just
anyway and those do exactly this: do the operation in the Just
cases and return Nothing
everywhere else.
The complete code
For reference and easier copy&paste here is the complete code:
{-# LANGUAGE OverloadedStrings #-}
module Expressions where
import Data.Maybe (fromMaybe)
import Data.String (IsString(..))
data Expr = ExprNum Double -- constants
| ExprVar String -- variables
| ExprAdd Expr Expr
| ExprSub Expr Expr
| ExprNeg Expr -- The unary '-' operator
| ExprMul Expr Expr
| ExprDiv Expr Expr
deriving Show
instance Num Expr where
a + b = ExprAdd a b
a * b = ExprMul a b
negate a = ExprNeg a
fromInteger n = ExprNum (fromInteger n)
abs _ = undefined
signum _ = undefined
instance Fractional Expr where
fromRational r = ExprNum (fromRational r)
a / b = ExprDiv a b
instance IsString Expr where
fromString = ExprVar
evaluate :: [(String, Double)] -> Expr -> Double
evaluate env (ExprMul a b) = evaluate env a * evaluate env b
evaluate env (ExprAdd a b) = evaluate env a + evaluate env b
evaluate env (ExprDiv a b) = evaluate env a / evaluate env b
evaluate env (ExprSub a b) = evaluate env a - evaluate env b
evaluate env (ExprNeg a) = negate $ evaluate env a
evaluate _ (ExprNum n) = n
evaluate env (ExprVar v) = fromMaybe 0 $ lookup v env
calculate :: Expr -> Expr
calculate (ExprMul a b) = operateOnNums ExprMul (*) a b
calculate (ExprAdd a b) = operateOnNums ExprAdd (+) a b
calculate (ExprDiv a b) = operateOnNums ExprDiv (/) a b
calculate (ExprSub a b) = operateOnNums ExprSub (-) a b
calculate (ExprNeg a) = operateOnNum ExprNeg negate a
calculate a = a
operateOnNums :: (Expr -> Expr -> Expr) -> (Double -> Double -> Double) -> Expr -> Expr -> Expr
operateOnNums def f a b = let a' = calculate a
b' = calculate b
in case (a',b') of
(ExprNum a'', ExprNum b'') -> ExprNum (f a'' b'')
_ -> def a' b'
operateOnNum :: (Expr -> Expr) -> (Double -> Double) -> Expr -> Expr
operateOnNum def f a = let a' = calculate a
in case a' of
ExprNum a'' -> ExprNum (f a'')
_ -> def a'
some examples
> calculate $ 3*4+5
ExprNum 17.0
> calculate $ 3*4+"a"
ExprAdd (ExprNum 12.0) (ExprVar "a")
> calculate $ 3*"a"+5
ExprAdd (ExprMul (ExprNum 3.0) (ExprVar "a")) (ExprNum 5.0)
> calculate $ 3*4+"a"
ExprAdd (ExprMul (ExprNum 3.0) (ExprNum 4.0)) (ExprVar "a")
> calculate $ "a" / 4.5
ExprDiv (ExprVar "a") (ExprNum 4.5)
> evaluate [("a",5)] (3*4+"a")
17.0
which is (I think) what you wanted to start with
remarks:
Don't forget to enable OverloadedStrings
in GHCi if you want to try this:
- start it with ghci -XOverloadedStrings
- :set -XOverloadedStrings
inside GHCi
这篇关于用户定义数据类型的操作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!