实现自定义运算符

时间:2015-11-07 20:57:59

标签: haskell

我有一个学校项目,我需要简化用户生成的数学表达式。我们还需要定义自己的基本运算符(:+:是加法,:*:是乘法等。)。

例如:

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

我正在寻找的是关于如何开始这一切的一些指导。我很自在地创建自己的函数,并使用常用函数(mapzipfoldr等等。我们最近开始学习类型类,仿函数和monad简介。

2 个答案:

答案 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是一个变量,可以匹配任何类型。因此,您尚未说xy值的类型为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."