大家好我是Haskell的新手,我想创建一个可以应用DeMorgan关于逻辑表达式的法则的Haskell程序。问题是我无法将给定表达式更改为新表达式(在应用DeMorgan定律后)
具体来说,这是我的数据结构
data LogicalExpression = Var Char
| Neg LogicalExpression
| Conj LogicalExpression LogicalExpression
| Disj LogicalExpression LogicalExpression
| Impli LogicalExpression LogicalExpression
deriving(Show)
我想创建一个函数,它接受“LogicalExpression”并在应用DeMorgan定律后返回“LogicalExpression”。
例如,每当我在logicalExpression中找到这种模式:Neg(Conj(Var'a')(Var'b'))时,我需要将其转换为Conj(Neg(Var'a')Neg(Var') b'))。
这个想法很简单,但是在haskell中很难实现,就像试图创建一个函数(让我们称之为Z)来搜索x并将其转换为y,所以如果Z被赋予“vx”它会转换它仅仅“vy”而不是数据结构“logicalExpression”中的字符串而不是x它采用我提到的模式并再次吐出整个logicalExpression但模式已更改。
P.S:我希望该函数采用任何 复杂 逻辑表达式,并使用DeMorgan的定律简化它
任何提示?
提前致谢。
答案 0 :(得分:7)
来自A Pattern for Almost Composable Functions的Bjorn Bringert的作品可以使这更容易,特别是如果你需要编写多个这样的规范化传递。它通常用Applicatives或2级类型编写,但为了简单起见,我将推迟:
根据您的数据类型
data LogicalExpression
= Var Char
| Neg LogicalExpression
| Conj LogicalExpression LogicalExpression
| Disj LogicalExpression LogicalExpression
| Impl LogicalExpression LogicalExpression
deriving (Show)
我们可以定义一个用于搜索非顶级子表达式的类:
class Compos a where
compos' :: (a -> a) -> a -> a
instance Compos LogicalExpression where
compos' f (Neg e) = Neg (f e)
compos' f (Conj a b) = Conj (f a) (f b)
compos' f (Disj a b) = Disj (f a) (f b)
compos' f (Impl a b) = Impl (f a) (f b)
compos' _ t = t
例如,我们可以消除所有影响:
elimImpl :: LogicalExpression -> LogicalExpression
elimImpl (Impl a b) = Disj (Not (elimImpl a)) (elimImpl b)
elimImpl t = compos' elimImpl t -- search deeper
然后我们可以应用它,就像luqui在上面所做的那样,寻找否定的连词和析取。而且,正如卢克指出的那样,在一次通过中做你所有的否定分布可能更好,我们还将包括否定蕴涵和双否定消除的归一化,产生否定正规形式的公式(假设我们已经消除了暗示)
nnf :: LogicalExpression -> LogicalExpression
nnf (Neg (Conj a b)) = Disj (nnf (Neg a)) (nnf (Neg b))
nnf (Neg (Disj a b)) = Conj (nnf (Neg a)) (nnf (Neg b))
nnf (Neg (Neg a)) = nnf a
nnf t = compos' nnf t -- search and replace
键是最后一行,表示如果上述其他情况都不匹配,请搜索可以应用此规则的子表达式。此外,由于我们将Neg
推入到术语中,然后对这些术语进行规范化,因此您应该只在叶子处使用否定变量,因为Neg
在另一个构造函数之前的所有其他情况将被标准化。
将使用更高级的版本
import Control.Applicative
import Control.Monad.Identity
class Compos a where
compos :: Applicative f => (a -> f a) -> a -> f a
compos' :: Compos a => (a -> a) -> a -> a
compos' f = runIdentity . compos (Identity . f)
和
instance Compos LogicalExpression where
compos f (Neg e) = Neg <$> f e
compos f (Conj a b) = Conj <$> f a <*> f b
compos f (Disj a b) = Disj <$> f a <*> f b
compos f (Impl a b) = Impl <$> f a <*> f b
compos _ t = pure t
这对您的特定情况没有帮助,但如果您需要返回多个重写结果,执行IO
或以其他方式在重写规则中进行更复杂的活动,这将非常有用。
您可能需要使用此功能,例如,您希望尝试将deMorgan法律应用于其应用位置的任何子集,而不是追求正常形式。
请注意,无论您正在重写哪种函数,在遍历期间使用的是适用的,甚至是信息流的方向性,compos
定义只需要为每种数据类型提供一次。
答案 1 :(得分:6)
如果我理解正确,你想要应用De Morgan的定律尽可能地将否定推入树中。你必须多次明确地递归树:
-- no need to call self on the top-level structure,
-- since deMorgan is never applicable to its own result
deMorgan (Neg (a `Conj` b)) = (deMorgan $ Neg a) `Disj` (deMorgan $ Neg b)
deMorgan (Neg (a `Disj` b)) = (deMorgan $ Neg a) `Conj` (deMorgan $ Neg b)
deMorgan (Neg a) = Neg $ deMorgan a
deMorgan (a `Conj` b) = (deMorgan a) `Conj` (deMorgan b)
-- ... etc.
在term-rewriting system中所有这些都会容易得多,但这不是Haskell的原因。
(顺便说一下,如果你在公式解析器中将P -> Q
翻译成not P or Q
并删除Impli
构造函数,生活会变得容易得多。公式中每个函数的个案数变为小。)
答案 2 :(得分:4)
其他人给予了很好的指导。但我会将其称为否定消除器,因此这意味着你有:
deMorgan (Neg (Var x)) = Neg (Var x)
deMorgan (Neg (Neg a)) = deMorgan a
deMorgan (Neg (Conj a b)) = Disj (deMorgan (Neg a)) (deMorgan (Neg b))
-- ... etc. match Neg against every constructor
deMorgan (Conj a b) = Conj (deMorgan a) (deMorgan b)
-- ... etc. just apply deMorgan to subterms not starting with Neg
我们可以通过归纳看到,在结果中,Neg
只会应用于Var
个字词,最多只能应用一次。
我喜欢将这样的转换视为消除器:即试图通过推卸它们来“摆脱”顶级某个构造函数的东西。将要消除的构造函数与每个内部构造函数(包括其自身)进行匹配,然后转发其余部分。例如,lambda演算评估器是Apply
消除器。 SKI转换器是Lambda
消除器。
答案 3 :(得分:2)
重点是deMorgan的递归应用程序。它与(例如)完全不同:
deMorgan' z@(Var x) = z
deMorgan' (Neg (Conj x y)) = (Disj (Neg x) (Neg y))
deMorgan' (Neg (Disj x y)) = (Conj (Neg x) (Neg y))
deMorgan' z@(Neg x) = z
deMorgan' (Conj x y) = Conj x y
deMorgan' (Disj x y) = Disj x y
不起作用:
let var <- (Conj (Disj (Var 'A') (Var 'B')) (Neg (Disj (Var 'D') (Var 'E'))))
*Main> deMorgan' var
Conj (Disj (Var 'A') (Var 'B')) (Neg (Disj (Var 'D') (Var 'E')))
这里的问题是你不在subexpressiosn(x和ys)中应用转换。