我尝试创建一个用于处理逻辑表达式的数据结构。乍一看,逻辑表达式看起来像Trees
,因此从树中组成它似乎是合理的:
data Property a = And (Property a) (Property a) |
Or (Property a) (Property a) |
Not (Property a) | Property a deriving (Show,Eq)
但这不是一个好主意,因为我的树的左右分支之间确实没有区别,因为A && B == B && A
嗯,也许List
甚至更好?
data Property a = Empty | And a (Property a) | Or a (Property a) | Not a (Property a) deriving Show
但在这种情况下,我无法真正形成'逻辑树',只能形成'逻辑列表'。
所以我需要一个类似于Tree
但没有左右“分支”的数据结构。
答案 0 :(得分:10)
我们将遵循Daniel Wagner的优秀建议并使用“天真的代表”(您的第一个代表),以及选择一种着名的正常形式的功能"。我们将使用algebraic normal form有两个原因。主要原因是代数正规形式不需要枚举Property
中保存的变量类型的所有可能值。代数范式也很简单。
我们将使用newtype ANF a = ANF [[a]]
表示不导出其构造函数的代数正规形式。每个内部列表都是其所有谓词的连接(和);如果内部列表为空,则始终为真。外部列表是其所有谓词的独占或排序;如果它是空的那就是假的。
module Logic (
ANF (getANF),
anf,
...
)
newtype ANF a = ANF {getANF :: [[a]]}
deriving (Eq, Ord, Show)
我们将努力使我们构建的任何ANF
都是规范的。我们将构建ANF
s,以便内部列表始终排序且唯一。外部列表也将始终排序。如果外部列表中有两个(或偶数个)相同的项目,我们将删除它们。如果外部列表中存在奇数个相同的项目,我们将删除除其中一个之外的所有项目。
使用Data.List.Ordered
包中data-ordlist的函数,我们可以清除表示连词xor-ing的列表列表到ANF
,列表已排序并重复删除。
import qualified Data.List.Ordered as Ordered
anf :: Ord a => [[a]] -> ANF a
anf = ANF . nubPairs . map Ordered.nubSort
nubPairs :: Ord a => [a] -> [a]
nubPairs = removePairs . Ordered.sort
where
removePairs (x:y:zs)
| x == y = removePairs zs
| otherwise = x:removePairs (y:zs)
removePairs xs = xs
逻辑表达式形成boolean algebra,它是一个带有额外分配律和补充(否定)的有界点阵。利用BoundedLattice
包中现有的lattices类,我们可以定义BooleanAlgebra
s的类
import Algebra.Lattice
class (BoundedLattice a) => BooleanAlgebra a where
complement :: a -> a
-- Additional Laws:
-- a `join` complement a == top
-- a `meet` complement a == bottom
-- a `join` (b `meet` c) == (a `join` b) `meet` (a `join` c)
-- a `meet` (b `join` c) == (a `meet` b) `join` (a `meet` c)
每当ANF a
有BooleanAlgebra
个实例时, a
形成Ord
,以便我们可以将ANF
保持为规范形式。
布尔代数的meet
是逻辑and
。逻辑and
分布在xor
上。我们将利用内部列表已经排序以快速将它们合并在一起的事实。
instance (Ord a) => MeetSemiLattice (ANF a) where
ANF xs `meet` ANF ys = ANF (nubPairs [Ordered.union x y | x <- xs, y <- ys])
使用De Morgan's laws,join
或逻辑or
可以使用meet
或逻辑and
来撰写。
instance (Ord a) => JoinSemiLattice (ANF a) where
xs `join` ys = complement (complement xs `meet` complement ys)
格子的top
始终为真。
instance (Ord a) => BoundedMeetSemiLattice (ANF a) where
top = ANF [[]]
格子的bottom
始终为假。
instance (Ord a) => BoundedJoinSemiLattice (ANF a) where
bottom = ANF []
逻辑and
和逻辑or
符合晶格吸收定律a join (a meet b) == a meet (a join b) == a
。
instance (Ord a) => Lattice (ANF a)
instance (Ord a) => BoundedLattice (ANF a)
最后,complement
操作是否定的,可以通过xor
来实现。
instance (Ord a) => BooleanAlgebra (ANF a) where
complement (ANF ([]:xs)) = ANF xs
complement (ANF xs) = ANF ([]:xs)
与Pointed
一起,我们可以使用BooleanAlgebra
来定义包含逻辑表达式的事物类Logical
。
import Data.Pointed
class Logical f where
toLogic :: (Pointed g, BooleanAlgebra (g a)) => f a -> g a
代数范式包含逻辑表达式。
xor :: BooleanAlgebra a => a -> a -> a
p `xor` q = (p `join` q) `meet` complement (p `meet` q)
instance Logical ANF where
toLogic = foldr xor bottom . map (foldr meet top . map point) . getANF
代数法线形式也是Pointed
,因此可以转换为使用toLogic
。
instance Pointed ANF where
point x = ANF [[x]]
toANF :: (Logical f, Ord a) => f a -> ANF a
toANF = toLogic
我们可以通过比较来判断是否有Logical
在逻辑上是等价的,看它在转换为规范代数范式时是否在结构上是等价的。
equivalent :: (Logical f, Logical g, Ord a) => f a -> g a -> Bool
equivalent a b = toANF a == toANF b
逻辑表达式应该形成一个布尔代数,它是一个带有附加分配律和补充(否定)的有界点。为了使Property
更接近于布尔代数,我们需要为格子的top
和bottom
边界添加两个元素。 top
始终为True
,bottom
始终为False
。对于始终为Trivial
的属性,我将为True
和Impossible
的属性调用这些False
。
data Property a
= And (Property a) (Property a)
| Or (Property a) (Property a)
| Not (Property a)
| Property a
| Trivial
| Impossible
deriving (Eq, Ord, Read, Show)
Property
是属性的抽象语法树。它派生的Eq
和Ord
实例只是结构上的相等。
Property
为Logical
,我们可以将其转换为Pointed
BooleanAlgebra
。
instance Logical Property where
toLogic (And a b) = (toLogic a) `meet` (toLogic b)
toLogic (Or a b) = (toLogic a) `join` (toLogic b)
toLogic (Not a) = complement (toLogic a)
toLogic (Property a) = point a
toLogic Trivial = top
toLogic Impossible = bottom
使用上一节中的equivalent
和我们的Logical
实例,我们可以检查两个属性是否与某些示例相同。
runExample :: (Ord a, Show a) => Property a -> Property a -> IO ()
runExample p q =
if p `equivalent` q
then putStrLn (show p ++ " == " ++ show q)
else putStrLn (show p ++ " /= " ++ show q)
main = do
runExample (point 'a' `meet` point 'b') (point 'b' `meet` point 'a')
runExample (point 'a' `meet` point 'b') (point 'b' `meet` point 'a' `meet` point 'a')
runExample (point 'a' `meet` point 'b') (point 'b' `meet` point 'a' `join` point 'a')
runExample (point 'a' `join` complement (point 'a')) (top)
runExample (point 'a' `meet` complement (point 'a')) (bottom)
这会产生以下输出。
And (Property 'a') (Property 'b') == And (Property 'b') (Property 'a')
And (Property 'a') (Property 'b') == And (And (Property 'b') (Property 'a')) (Pr
operty 'a')
And (Property 'a') (Property 'b') /= Or (And (Property 'b') (Property 'a')) (Pro
perty 'a')
Or (Property 'a') (Not (Property 'a')) == Trivial
And (Property 'a') (Not (Property 'a')) == Impossible
要使用这些示例,我们还需要BooleanAlgebra
的{{1}}和Pointed
个实例。 Property
定律的等价关系是等价的解释,而不是BooleanAlgebra
的结构平等。
Property
每个布尔函数,以及每个有限instance MeetSemiLattice (Property a) where
meet = And
instance BoundedMeetSemiLattice (Property a) where
top = Trivial
instance JoinSemiLattice (Property a) where
join = Or
instance BoundedJoinSemiLattice (Property a) where
bottom = Impossible
instance Lattice (Property a)
instance BoundedLattice (Property a)
instance BooleanAlgebra (Property a) where
complement = Not
instance Pointed Property where
point = Property
,在Property
中都有唯一表示。 ANF
的{{1}}和BooleanAlgebra
个实例证明,所有Pointed
,以及ANF
索引的每个有限Logical a
和布尔函数都有Property a
中的表示。设a
为ANF a
的居民数量。 k
布尔变量有a
个可能的布尔函数。 2^(2^k)
的每个内部列表都是一组k
s;有ANF
个可能的a
个集合,因此有2^k
个可能的内部列表。 a
的外部列表是一组内部列表;每个内部列表最多可以在外部列表中出现一次。可能有2^k
ANF
个。由于每个布尔函数都在2^(2^k)
中有一个表示,并且只有ANF a
的可能居民与布尔函数一样多,因此每个布尔函数在{{{}中都有唯一表示。 1}}。
ANF
的{{1}}实例是一个结构顺序,除了相等之外,与晶格结构引起的偏序无关。
代数法线形式可以指数大于其输入。 ANF
变量列表的分离大小为ANF
。例如,Ord
为ANF
,n
包含.5*n*2^n
个length . getANF . foldr join bottom . map point $ ['a'..'g']
个不同变量的出现次数。
答案 1 :(得分:1)
我建议使用SAT / SMT解算器进行等效性检查。通常,这些类型的检查可能非常昂贵(NP完全),并且任何类型的正常形式的转换都可能导致表示的指数爆炸。 SAT / SMT求解器具有用于处理此类问题的自定义算法,并且最好使用这样的求解器。翻译Property
的两个实例并询问它们在所有变量赋值下是否相同将是微不足道的。 SBV库(https://hackage.haskell.org/package/sbv)可用于从高级Haskell进行转换。以下问题的答案有关于如何执行此操作的一些详细信息:SAT solving with haskell SBV library: how to generate a predicate from a parsed string?
答案 2 :(得分:0)
如果要反映逻辑连接词和(&&
)和或的关联性,请使用关联的数据结构,如list:< / p>
data Property a = And [Property a] | ...
如果您还想要交换性(A && B == B && A
),请使用Data.Set。
我记得这个方便的图表,如果你想要某些属性,可以使用哪些类型:
+-----------+-----------+----------+----------
|Associative|Commutative|Idempotent| type
+-----------+-----------+----------+----------
|(a+b)+c= | a+b=b+a | a+a=a |
| a+(b+c)| | |
+-----------+-----------+----------+----------
| no | no | no |binary trees
| no | yes | no | “mobiles”
| yes | no | no |lists (arrays)
| yes | yes | no |multisets (bags)
| yes | yes | yes |sets
+-----------+-----------+----------+----------
Guy Steele's Lecture的第51页。