创建Expr类的[StackExp]实例

时间:2013-05-04 15:37:57

标签: class haskell instance

我为算术运算创建了类Expr

class Expr a where
  mul :: a -> a -> a 
  add :: a -> a -> a
  lit :: Integer -> a

我想“解析”这样的东西:mul(add(lit 3)(lit 2))(lit 4)=(3 + 2)* 4

我有数据类型:

data StackExp = PushI Integer
              | PushB Bool
              | Add
              | Mul
              | And
              | Or
                deriving Show

type Program = [StackExp] --i use this type for function of stack calculator later

我的任务是:我需要为Expr

类型设置Program的实例

更具体 - 我想进行这种转变: mul ( add (lit 3) (lit 2)) (lit 4) - >>> [PushI 2, PushI 3, Add, PushI 4, Mul]

我遇到问题,因为我在实例声明的输出中收到[[StackExp]]

我的尝试:

instance Expr Program where
  lit n = (PushI n):[]
  add exp1 exp2 = exp1:(exp2:[Add]) 
  mul exp1 exp2 = exp1:(exp2:[Mul])

我不知道如何将所有子表达式连接到列表

----------------编译器错误看起来像这样------------------------

Couldn't match type `[StackExp]' with `StackExp'
    Expected type: StackExp
      Actual type: Program

..........

1 个答案:

答案 0 :(得分:3)

所以,你基本上想要做的是你想要从表达式语言的抽象语法(类型类Expr)编译为一个简单的堆栈机器的代码(类型{{{ 1}})。

您遇到的一个问题是,在Haskell 98或Haskell 2010中,您无法声明StackExpr类的实例。例如,GHC会抱怨

[StackExpr]

为了解决这个问题,您可以将Illegal instance declaration for `Expr [StackExp]' (All instance types must be of the form (T a1 ... an) where a1 ... an are *distinct type variables*, and each type variable appears at most once in the instance head. Use -XFlexibleInstances if you want to disable this.) In the instance declaration for `Expr [StackExp]' 定义为类型同构(而不是现在的类型同义词):

Program

然后使Program成为Expr类的一个实例。 (或者,您可以按照上面的错误消息的建议启用newtype Program = P [StackExp] deriving Show 。)

现在我们可以编写所需的实例声明:

FlexibleInstances

也就是说,对于乘法和加法,我们首先编译操作数,然后分别生成instance Expr Program where mul (P x) (P y) = P (x ++ y ++ [Mul]) add (P x) (P y) = P (x ++ y ++ [Add]) lit n = P [PushI n] Mul指令;对于文字,我们生成相应的推送指令。

通过这个声明,我们得到了你的例子:

Add

(在你的例子中不完全。你将操作数的顺序交换为> mul (add (lit 3) (lit 2)) (lit 4) :: Program P [PushI 3,PushI 2,Add,PushI 4,Mul] 。由于加法是可交换的,我将假设这个版本也是可接受的。)


当然,为堆栈程序编写一个小评估器会更有趣:

Add

(请注意,我忽略了在这里处理布尔值的指令,因为你似乎只是处理整数表达式。)

现在我们已经为您的例子

eval :: Program -> [Integer]
eval (P es) = go es []
  where
    go [] s                   = s
    go (PushI   n : es) s     = go es (n : s)
    go (Add : es) (m : n : s) = go es ((m + n) : s)
    go (Mul : es) (m : n : s) = go es ((m * n) : s)

这似乎是正确的。