我正在实现一个Expression求解器,但我在模式匹配方面遇到了一些问题。 我有以下代码
data Expression a where
Const ∷ Int → Expression Int
Add ∷ Expression Int → Expression Int → Expression Int
Sub ∷ Expression Int → Expression Int → Expression Int
eval ∷ Expression a → a
eval (Const a) = a
eval (Add exp1 exp2) = (val1 + val2)
where
val1 = eval exp1
val2 = eval exp2
eval (Sub exp1 exp2) = (val1 - val2)
where
val1 = eval exp1
val2 = eval exp2
但是因为eval Add和eval Sub非常相似,我可能想要另一个操作,但我做了更通用的实现,但是我遇到了一些问题。 我虽然喜欢
data Op = Add | Sub
data Expression a where
Const ∷ Int → Expression Int
Op ∷ Expression Int → Expression Int → Expression Int
eval (Op exp1 exp2) = case Op of
Add → (val1 + val2)
Sub → (val1 - val2)
where
val1 = eval exp1
val2 = eval exp2
但它不起作用。可以这样做吗? 提前致谢
答案 0 :(得分:7)
这不起作用,因为您将Op
定义为数据构造函数
和一种类型。类型Op
有两个构造函数Add
和Sub
,但是
Expression
类型具有Op
构造函数。这段代码令两者混淆。
eval
函数的case语句尝试匹配值
Op
,但Op
是一个构造函数,在此上下文中接受两个参数,
所以你不能在它上进行模式匹配。我怀疑你会选择类似的东西
此
data Op = Add | Sub
data Expression a where
Const :: Int -> Expression Int
Op :: Op -> Expression Int -> Expression Int -> Expression Int
eval (Const c) = c
eval (Op op exp1 exp2) = case op of
Add -> (val1 + val2)
Sub -> (val1 - val2)
where
val1 = eval exp1
val2 = eval exp2
您必须在Op
构造函数中包含一个表示内容的字段
要进行操作。因为你必须匹配该操作
无论如何,坚持原来的定义可能会更好
Expression
。
另一种更简单,更容易扩展的可能性就像是 以下
data Expression a where
Const :: Int -> Expression Int
Op :: (a -> b -> c) -> Expression a -> Expression b -> Expression c
eval :: Expression a -> a
eval (Const c) = c
eval (Op f exp1 exp2) = f (eval exp1) (eval exp2)
其中Op
用它包装实际函数。你无法做到
做一些好的事情,比如打印出表达式并知道它的功能
虽然对应。
答案 1 :(得分:4)
重审评论:
data Op a b c where
Add :: Op Int Int Int
Sub :: Op Int Int Int
Less :: Op Int Int Bool
interpretOp :: Op a b c -> a -> b -> c
interpretOp Add = (+)
interpretOp Sub = (-)
interpretOp Less = (<)
data Expression a where
Const :: Int -> Expression Int
Op :: Op a b c -> Expression a -> Expression b -> Expression c
eval :: Expression a -> a
eval (Const x) = x
eval (Op op a b) = interpretOp op (eval a) (eval b)
答案 2 :(得分:0)
对luqui的回答嗤之以鼻:
{-# LANGUAGE TypeFamilies, MultiParamTypeClasses, GADTs #-}
class OpLike op a b where
type Result op a b
interpret :: op -> a -> b -> Result op a b
data Expression a where
Const :: a -> Expression a
Op :: OpLike op a b => op -> Expression a -> Expression b -> Expression (Result op a b)
eval :: Expression a -> a
eval (Const x) = x
eval (Op op a b) = interpret op (eval a) (eval b)
现在,您可以在程序中的任何位置添加运算符,而无需更改luqui的Op
数据类型。这是一个非常人为的例子:
data Add = Add
add x y = Op Add x y
instance OpLike Add Int Int where
type Result Add Int Int = Int
interpret Add x y = x + y
instance OpLike Add Int Bool where
type Result Add Int Bool = String
interpret Add x y = if y then reverse (show x) else show x
example = (Const (3::Int) `add` Const (10::Int)) `add` (Const True)
example
类型为Expression String
,eval
为"31"
: - )