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我已经添加了一句话来解决这个问题。我想知道为什么这被否决了?这种方法有什么问题吗?(除了变量捕获和替换非自由事件之外,这与迄今为止的其他答案一样常见)