Are you sure, you need the Nil
constructor? That's how flatten
looks without it:
cutAndAppend :: Prop -> [Prop] -> [Prop]
cutAndAppend (And ps) qs = ps ++ qs
cutAndAppend p qs = p : qs
cutOrAppend :: Prop -> [Prop] -> [Prop]
cutOrAppend (Or ps) qs = ps ++ qs
cutOrAppend p qs = p : qs
flatten (Var v) = Var v
flatten (Not p) = Not (flatten p)
flatten (And ps) = And (foldr (cutAndAppend . flatten) [] ps)
flatten (Or ps) = Or (foldr (cutOrAppend . flatten) [] ps)
Equivalently you can write
flatten (And ps) = And (foldr cutAndAppend [] $ map flatten ps)
flatten (Or ps) = Or (foldr cutOrAppend [] $ map flatten ps)
i.e. flatten every p
in ps
, and, for example, in the Or
case transform the list
[Var "x", Or [Var "y", And [Var "z"]], And [Var "x"]]
into
[Var "x", Var "y", And [Var "z"], And [Var "x"]]
But if you really want Nil
, it's better to treat Nil
as Nothing
, because it's more idiomatic and there are some useful functions, acting on Maybe
s, in the standard library.
nonEmpty :: [a] -> Maybe [a]
nonEmpty [] = Nothing
nonEmpty xs = Just xs
flatten' :: Prop -> Maybe Prop
flatten' Nil = Nothing
flatten' (Var v) = Just (Var v)
flatten' (Not p) = Not <$> flatten' p
flatten' (And ps) = And <$> nonEmpty (foldr cutAndAppend [] $ mapMaybe flatten' ps)
flatten' (Or ps) = Or <$> nonEmpty (foldr cutOrAppend [] $ mapMaybe flatten' ps)
Nil
s are filtered by mapMaybe flatten'
. Other stuff is the same. flatten
then is simply
flatten :: Prop -> Prop
flatten = fromMaybe Nil . flatten'
i.e. Nothing
back to Nil
and Just p
to p
.
Some examples:
vx = Var "x"; vy = Var "y"; vz = Var "z"
main = mapM_ (print . flatten)
[ And [Or [Nil], Nil, And [Nil]]
-- Nil
, And [Or [vx, vy], Or [vx, Or [vz, Nil]]]
-- And [Or [Var "x",Var "y"],Or [Var "x",Var "z"]]
, Or [Or [vx, vy], Or [vx, Or [vz, Nil]]]
-- Or [Var "x",Var "y",Var "x",Var "z"]
, Not (And [And [Or [Or [Nil], Or [Not vx]], Nil, And [vy]]])
-- Not (And [Or [Not (Var "x")],Var "y"])
]
EDIT
The first code doesn't remove expressions like Or []
, and both the codes do not replace expressions like And [p]
with p
. It's not hard to recover this behavior, though. Relevant cases are:
smart :: ([Prop] -> Prop) -> [Prop] -> Maybe Prop
smart cons [] = Nothing
smart cons [x] = Just x
smart cons xs = Just (cons xs)
flatten' (And ps) = smart And $ foldr cutAndAppend [] $ mapMaybe flatten' ps
flatten' (Or ps) = smart Or $ foldr cutOrAppend [] $ mapMaybe flatten' ps
Some tests:
main = mapM_ (print . flatten)
[ Not (And [And [Or [Or [Nil], Or [Not vx]], Nil, And [vy]]])
-- Not (And [Not (Var "x"),Var "y"])
, And []
-- Nil
, Or [And [vx]]
-- Var "x"
, And [vx, Or []]
-- Var "x"
, And [Or [And [vx, Or [], vy, Nil]], And [vz, Or [vx]]]
-- And [Var "x",Var "y",Var "z",Var "x"]
]
The whole code: http://ideone.com/0DwEae
A slightly modified version without Maybe
s: http://ideone.com/1yRDXo