Haskell函数中的模式匹配
我有许多方法的定义中都有样板代码,请看上面的示例Haskell函数中的模式匹配,haskell,pattern-matching,Haskell,Pattern Matching,我有许多方法的定义中都有样板代码,请看上面的示例 replace:: Term -> Term -> Formula -> Formula replace x y (Not f) = Not $ replace x y f replace x y (And f g) = And (replace x y f) (replace x y g) replace x y (Or f g) = Or (replace x y
replace:: Term -> Term -> Formula -> Formula
replace x y (Not f) = Not $ replace x y f
replace x y (And f g) = And (replace x y f) (replace x y g)
replace x y (Or f g) = Or (replace x y f) (replace x y g)
replace x y (Biimp f g) = Biimp (replace x y f) (replace x y g)
replace x y (Imp f g) = Imp (replace x y f) (replace x y g)
replace x y (Forall z f) = Forall z (replace x y f)
replace x y (Exists z f) = Exists z (replace x y f)
replace x y (Pred idx ts) = Pred idx (replace_ x y ts)
如您所见,replace
函数的定义遵循一种模式。我希望函数具有相同的行为,简化他的定义,可能使用一些模式匹配,可能在参数上使用通配符\ucode>或X
,例如:
replace x y (X f g) = X (replace x y f) (replace x y g)
为了避免以下定义:
replace x y (And f g) = And (replace x y f) (replace x y g)
replace x y (Or f g) = Or (replace x y f) (replace x y g)
replace x y (Biimp f g) = Biimp (replace x y f) (replace x y g)
replace x y (Imp f g) = Imp (replace x y f) (replace x y g)
有什么办法吗?忘了函数的用途吧,它可以是任何东西 我不完全确定这是您想要的,但您可以做以下几点。这个想法是,你可以考虑将公式抽象为另一种类型(通常是<代码>术语<代码>)。然后,您可以定义映射公式的含义。我试图复制您的数据定义,尽管我在公式
方面有一些问题-即所有构造函数似乎都需要另一个公式
{-# LANGUAGE DeriveFunctor #-}
data Formula a
= Not (Formula a)
| And (Formula a) (Formula a)
| Or (Formula a) (Formula a)
| Biimp (Formula a) (Formula a)
| Imp (Formula a) (Formula a)
| Forall a (Formula a)
| Exists a (Formula a)
| Pred a (Formula a)
deriving (Functor)
data Term = Term String {- However you define it, doesn't matter -} deriving (Eq)
replace :: (Functor f, Eq a) => a -> a -> f a -> f a
replace x y = fmap (\z -> if x == z then y else z)
有趣的是,现在replace
函数可以应用于任何函数,它甚至可以作为列表的replace
replace 3 9 [1..6] = [1,2,9,4,5,6]
编辑事后想一想,如果您在公式中的术语可以隐藏的地方实施替换样式替换(通常的范围规则),您可能会执行以下操作:
replace' :: (Eq a) => a -> a -> Formula a -> Formula a
replace' x y f@(Forall z _) | x == z = f
replace' x y f@(Exists z _) | x == z = f
replace' x y f@(Pred z _) | x == z = f
replace' x y formula = fmap (replace' x y) formula
这不是一个有趣的问题,但也不是一个简单的问题。如果有许多构造函数应该以统一的方式处理,那么应该让数据类型反映这一点
data BinOp = BinAnd | BinOr | BinBiimp | BinImp
data Quantifier = QForall | QExists
data Formula = Not Formula
| Binary BinOp Formula Formula -- key change here
| Quantified Quantifier Formula
| Pred Index [Formula]
现在,所有二进制运算符的模式匹配更容易:
replace x y (Binary op f g) = Binary op (replace x y f) (replace x y g)
要保留现有代码,您可以启用模式同义词
,并将和
、或
等的旧版本重新定义为存在:
pattern And x y = Binary BinAnd x y
pattern Forall f = Quantified QForall f
抽象递归数据结构的模式:
import Data.Functor.Foldable
data FormulaF t
= Not t
| And t t
| Or t t
| Biimp t t
| Imp t t
| Forall A t
| Exists A t
| Pred B C
deriving (Functor, Foldable, Traversable)
type Formula = Fix FormulaF
replace :: Term -> Term -> Formula -> Formula
replace x y = cata $ \case ->
Pred idx ts -> Pred idx (replace_ x y ts)
f -> f
顺便说一句,请注意replace xy(Forall x(fx))=Forall x(fy)
:可能DeriveFunctor
或创建它的显式实例?然后,上面的结果可能是replace xy=fmap(\z->如果x==z,那么y else x)
。当然,这意味着公式
将具有种类*->*
,但这听起来很合理Formula Bool
将是一个布尔公式。您可以将Formula
作为的一个实例,这会有帮助吗?我认为最后一个是行不通的fmap
将期望某种类型的a->a
,但您正在处理某种类型的Formula a->Formula a
。(但这里剩下的建议是可靠的。)我喜欢这样,但坏消息是,我必须重写每一个方法。@jonaprieto我已经添加了一句话来解决这个问题。我想知道为什么这被否决了?这种方法有什么问题吗?(除了变量捕获和替换非自由事件之外,这与迄今为止的其他答案一样常见)