对给定数据类型集的通用转换

时间:2017-09-14 20:32:06

标签: haskell uniplate

如果我有一个表示命题逻辑子集的数据类型,例如

data Prop = Lit String
          | Neg Prop
          | And Prop Prop
          | Or Prop Prop

是否有简单的方法在[[Prop]]上进行通用转换? E.g。

  • [[And a b, c]]替换为[[a, b, c]]
  • [[Or a b, c]]替换为[[a], [b], [c]]
  • 删除包含Neg aa的子列表的出现,例如将[[Neg a, x, a], [b]]转入[[b]]

这感觉就像接近例如uniplate确实如此,但“升级两级”。

1 个答案:

答案 0 :(得分:1)

我认为你的第二条规则是错误的,你真的应该说:

  • [[Or a b],[c]]替换为[[a],[b],[c]]

或者:

  • [[Or a b, c]]替换为[[a,c],[b,c]]

换句话说,我假设您正在尝试将Prop转换为替代代表[[Prop]],其中第一级列表是"或"第二级列表是"和" s,所有术语都是文字或Neg - 文字。因此,您试图想象如何应用一堆通用结构规则来进行转换,如:

[[And a (Or b c)]]
[[a, Or b c]]        -- apply "And" rule
[[a,b],[a,c]]        -- apply some kind of "Or" distribution rule

如果是这样,通用转换并没有多大用处。使用当前的数据类型,您只能将这些转换应用于顶级表达式。例如,此处没有明显的方法来应用Or规则:

[[And a (And b (Or c d))]]

没有首先应用And规则几次。如果您更改数据类型以添加​​L2 [[Prop]]构造函数,那么您可以将上面的表达式转换为:

[[And a (And b (L2 [[c],[d]]))]]   -- apply "Or" rule

它不清楚是什么让你买。

最终,我不认为这是正确的做法......

您对Prop数据类型中的介词逻辑有完全充分的表示;你有一个理想的最终代表。不是尝试使用零碎的通用转换将Prop表示转换为最终表示,而是使用标准的递归Prop-to-Prop转换将Prop表示转换为规范Prop形式,并且翻译作为最后一步。

这里,一个合理的规范形式是:

Or e1 (Or e2 (... (Or e3 e4)))

每个ek的形式为:

And t1 (And t2 (... (And t3 t4)))

并且每个tk都是Lit _Neg (Lit _)。显然,这个规范形式可以很容易地转换为[[Prop]]所需的最终表示。

我在下面提供了一个可能的解决方案。我没有看到通过泛型转换简化事物的机会。大多数模式匹配似乎都在进行非平凡的工作。

可能的解决方案

经过一些序言:

import Data.List

data Prop = Lit String
          | Neg Prop
          | And Prop Prop
          | Or Prop Prop
          deriving (Eq)

然后,将任意Prop转换为此规范形式的一种方法是首先将所有Neg向下推到字面值:

pushNeg :: Prop -> Prop
pushNeg = push False
  where
    -- de Morgan's laws
    push neg (And x y) = (if neg then Or else And) (push neg x) (push neg y)
    push neg (Or x y)  = (if neg then And else Or) (push neg x) (push neg y)
    -- handle Neg and Lit
    push neg (Neg y) = push (not neg) y
    push neg (Lit l) = if neg then Neg (Lit l) else Lit l

然后将所有And向下推到它们之上。这样做是很难的,但我认为以下是正确的,即使它在某些情况下做了一些不必要的工作:

pushAnd :: Prop -> Prop
pushAnd (Or x y) = Or (pushAnd x) (pushAnd y)
pushAnd (And x y)
  = let x' = pushAnd x
    in  case x' of
          Or u v -> Or (pushAnd (And u y)) (pushAnd (And v y))
          _ -> let y' = pushAnd y
               in case y' of
                    Or u v -> Or (pushAnd (And x' u)) (pushAnd (And x' v))
                    _ -> And x' y'
pushAnd x = x

然后以递归方式将所有AndOr子句设为右关联:

rassoc :: Prop -> Prop
rassoc (Or (Or x y) z)   = rassoc (Or x (Or y z))
rassoc (Or x        z)   = Or (rassoc x) (rassoc z)
rassoc (And (And x y) z) = rassoc (And x (And y z))
rassoc (And x         z) = And x (rassoc z)
rassoc x = x

最后将规范表单转换为最终表示形式(删除不一致的条款和重复的条款,而我们在其中):

translate :: Prop -> [[Prop]]
translate = nub . map nub . filter consistent . doOr
  where
    doOr x = case x of
      Or x y -> doAnd x : doOr y
      x      -> doAnd x : []
    doAnd x = case x of
      And x y -> x : doAnd y
      x       -> x : []
    consistent lits =
      let (falses, trues) = partition isNeg lits
          falses' = map (\(Neg (Lit l)) -> l) falses
          trues'  = map (\     (Lit l)  -> l) trues
      in null (intersect falses' trues')
    isNeg (Neg x) = True
    isNeg _       = False

整个管道是:

final :: Prop -> [[Prop]]
final = translate . rassoc . pushAnd . pushNeg

以及一些测试代码:

a = Lit "a"
b = Lit "b"
c = Lit "c"
d = Lit "d"
e = Lit "e"

-- Show instance, but only for `final` forms
instance Show Prop where
  show (Lit x) = x
  show (Neg (Lit x)) = '~':x

main :: IO ()
main = do print $ final (Neg a)
          print $ final (Or a b)
          print $ final (Or a a)
          print $ final (And a b)
          print $ final (And (Or (And (Or a b) c) d) e)
          print $ final (And (Or (Or a b) c) (Neg (And a (Or b d))))

输出:

[[~a]]
[[a],[b]]
[[a]]
[[a,b]]
[[a,c,e],[b,c,e],[d,e]]
[[a,~b,~d],[b,~a],[c,~a],[c,~b,~d]]

还有一些进一步简化的机会,如:

final (And a (Or a b))

提供最终表单[[a],[a,b]],而不只是[[a]]