我有一个学校项目,我需要简化用户生成的数学表达式。我们还需要定义自己的基本运算符(:+:
是加法,:*:
是乘法等。)。
例如:
Prelude> Const 9 :+: Const 3 :+: Const 2
Const 14
Prelude> Const 14 :*: Var 'x' :+: Const 10 :*: Var 'x'
Const 24 :*: Var 'x'
这是我到目前为止所做的:
infixl 4 :+:
infixl 5 :*:, :/:
infixr 6 :^:
data Expr a = Var Char
| Const a
| (Expr a) :+: (Expr a)
| (Expr a) :*: (Expr a)
| (Expr a) :^: (Expr a)
| (Expr a) :/: (Expr a)
deriving (Show, Eq)
我为我的加法运算符试过这样的事情但没有成功:
class (Num a) => Expr a where
(:+:) :: a -> a -> a
instance Expr a where
x :+: y = x + y
我正在寻找的是关于如何开始这一切的一些指导。我很自在地创建自己的函数,并使用常用函数(map
,zip
,foldr
等等。我们最近开始学习类型类,仿函数和monad简介。
答案 0 :(得分:3)
您会混淆两个截然不同的主题(如果您粘贴了错误消息,这可能会更早出现并获得更好的反馈)。让我们看看提供给你的是什么 - Expr
数据类型和类。
您获得了:
data Expr a = Var Char
| Const a
| (Expr a) :+: (Expr a)
| (Expr a) :*: (Expr a)
| (Expr a) :^: (Expr a)
| (Expr a) :/: (Expr a)
deriving (Show, Eq)
这里我们可以看到构造函数是基本的算术运算。换句话说,:+:
是Expr a -> Expr a -> Expr a
类型的构造函数,其他类似的构造函数。
现在你继续尝试定义一个函数,即:+:
:
class (Num a) => Expr a where
(:+:) :: a -> a -> a
instance Expr a where
x :+: y = x + y
这里有一些问题。
Expr
的名称与上述类型相同,与上面定义的数据无关。:+:
不是合法的函数名称。与数据构造函数不同,函数必须是有效变量,因此不能以大写或冒号字符开头。a
是一个变量,可以匹配任何类型。因此,您尚未说x
和y
值的类型为Expr a
。我认为您正在尝试为每个运算符创建一个类,并为每个构造函数创建一个实例(:+:
,:*:, etc). This is a broken, or at least overly verbose, solution. The only reason to use classes are if there are multiple type that require instances - you only have the one
Expr`类型。
不需要类型类来解决这个问题,而是可以使用传统函数计算表达式,就像删除的答案一样。
以前删除的答案似乎被删除了,并且需要为每个变量绑定环境。我对问题的解读是不同的,我只是将表达式评估为一个简单的字符串。
evaluate :: Show a => Expr a -> String
evaluate (Var c) = [c]
evaluate (a :+: b) = undefined
evaluate (a :*: b) ... so on and so forth ...
在上面你的模式匹配你的Expr a
以确定操作,然后根据具体情况,根据需要评估子表达式,然后以依赖于构造函数的方式组合它们({{1}与+
等)。
答案 1 :(得分:0)
我能够实施这个项目,我很感激帮助我开始朝着正确的方向前进。我会在这里发布我的解决方案以防万一有兴趣。它可能不漂亮,但它的工作原理!
在completeCleanUp
的帮助下, cleanUp
简化了Expr。
plugIn
需要一些Char 'x'
,并使用提供的数字替换所有Var 'x'
。最后evalExpr
返回Expr。
请注意,不需要实现变量简化,因此Const 9 :*: Var 'x' :+: Const 10 :*: Var 'x'
不会进一步减少。
cleanUp :: (Eq a, Floating a) => Expr a -> Expr a
cleanUp (Const a) = Const a
cleanUp (Var a) = Var a
cleanUp (Const a :*: (Const b :*: c)) = Const (a * b) :*: cleanUp c
cleanUp (Const a :*: c :*: Const b) = Const (a*b) :*: cleanUp c
cleanUp (c :*: Const a :*: Const b) = Const (a*b) :*: cleanUp c
cleanUp (Const a :*: (b :+: c)) = (Const a :*: cleanUp b) :+: (Const a :*: cleanUp c)
cleanUp (Const 0 :/: a) = Const 0
cleanUp (a :/: Const 0) = error "Error: Division by 0 not allowed."
cleanUp (a :/: b) | a == b = Const 1.0
cleanUp (a :^: Const 1) = cleanUp a
cleanUp (Const 1 :^: a) = cleanUp a
cleanUp (a :^: Const 0) = Const 1.0
cleanUp (Const 0 :^: a) = Const 1.0
cleanUp ((c :^: Const b) :^: Const a) = cleanUp c :^: Const (a*b)
cleanUp (Const a :^: Const b) = Const (a ** b)
cleanUp (Const a :/: Const b) = Const (a / b)
cleanUp (a :*: Const 1) = cleanUp a
cleanUp (Const 1 :*: a) = cleanUp a
cleanUp (a :*: Const 0) = Const 0
cleanUp (Const 0 :*: a) = Const 0
cleanUp (Const a :*: Const b) = Const (a * b)
cleanUp (a :+: Const 0) = cleanUp a
cleanUp (Const 0 :+: a) = cleanUp a
cleanUp (Const a :+: Const b) = Const (a + b)
cleanUp (Var a :^: b) = Var a :^: cleanUp b
cleanUp (a :^: Var b) = cleanUp a :^: Var b
cleanUp (Var a :+: b) = Var a :+: cleanUp b
cleanUp (a :+: Var b) = cleanUp a :+: Var b
cleanUp (Var a :*: b) = Var a :*: cleanUp b
cleanUp (a :*: Var b) = cleanUp a :*: Var b
cleanUp (Var a :/: b) = Var a :/: cleanUp b
cleanUp (a :/: Var b) = cleanUp a :/: Var b
cleanUp (a :^: b) = cleanUp a :^: cleanUp b
cleanUp (a :/: b) = cleanUp a :/: cleanUp b
cleanUp (a :*: b) = cleanUp a :*: cleanUp b
cleanUp (a :+: b) = cleanUp a :+: cleanUp b
completeCleanUp :: (Eq a, Floating a) => Expr a -> Expr a
completeCleanUp exp
| exp == cleanUp exp = exp
| otherwise = completeCleanUp (cleanUp exp)
plugIn :: Char -> a -> Expr a -> Expr a
plugIn char num (Var c) | char == c = Const num
plugIn char num (a :^: b) = plugIn char num a :^: plugIn char num b
plugIn char num (a :/: b) = plugIn char num a :/: plugIn char num b
plugIn char num (a :*: b) = plugIn char num a :*: plugIn char num b
plugIn char num (a :+: b) = plugIn char num a :+: plugIn char num b
plugIn char num a = a
evalExpr :: (Eq a, Floating a) => Char -> a -> Expr a -> a
evalExpr char num exp = case (completeCleanUp . plugIn char num $ exp) of
(Const x) -> x
_ -> error "Cannot further simplify solution."