我为算术运算创建了类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
..........
答案 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)
这似乎是正确的。