Структура данных для логических выражений в Haskell
Я пытаюсь создать структуру данных для работы с логическими выражениями. На первый взгляд, логические выражения выглядят как 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
но без левого и правого "ветвления".
3 ответов
мы собираемся следовать превосходному предложению Даниэля Вагнера и использовать "наивное представление (ваше первое), а также функцию, которая выбирает одну из известных нормальных форм". Мы собираемся использовать алгебраическая нормальная форма по двум причинам. Главная причина заключается в том, что алгебраическая нормальная форма не требует перечисления всех возможных значений переменной, которые хранятся в Property
. Алгебраическая нормальная форма также довольно проста.
Алгебраические Нормально Форма
мы представим алгебраическую нормальную форму с newtype ANF a = ANF [[a]]
это не экспортирует его конструктор. Каждый внутренний список является соединением (и-ing) всех его предикатов; если внутренний список пуст, он всегда истинен. Внешний список-это исключение или исключение всех его предикатов; если он пуст, он ложен.
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
логические выражения образуют булевой алгебры, которая является ограниченной решеткой с дополнительным распределительным законом и дополнением (отрицанием). Использование существующих BoundedLattice
класс решетки пакета, мы можем определить класс 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
S форма 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])
используя законы де Моргана, the 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
ing по true.
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
преобразование свойства в ANF
логические выражения должны образовывать булеву алгебру, которая является ограниченной решеткой с дополнительным распределительным законом и дополнением (отрицанием). Сделать 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
экземпляры всего структурного равенства.
A Property
is 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
и 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
. The BooleanAlgebra
и Pointed
экземпляров ANF
показала, что все Logical a
, и, таким образом, каждая конечная Property a
и Булева функция, индексированная a
, имеет представительства в ANF a
. Пусть k
быть числом жителей a
. Есть 2^(2^k)
возможные булевы функции k
логические переменные. Каждый внутренний список ANF
набор a
s; есть 2^k
возможные наборы a
s и таким образом 2^k
возможные внутренние списки. Внешний список ANF
- это набор внутренних списков; каждый внутренний список может появиться не более одного раза во внешнем списке. Есть 2^(2^k)
возможно ANF a
s. Поскольку каждая Булева функция имеет представление в ANF
и есть только как можно больше жителей ANF
поскольку существуют булевы функции, каждая Булева функция имеет уникальный представительство в ANF
.
отказ от ответственности
на Ord
экземпляр ANF
является структурным порядком и, кроме равенства, не имеет ничего общего с частичными порядками, индуцированными решеточной структурой.
алгебраическая нормальная форма может быть гораздо больше, чем ее ввод. Дизъюнкция списка n
переменные имеют размер .5*n*2^n
. Например, length . getANF . foldr join bottom . map point $ ['a'..'g']
is 127
и foldr join bottom . map point $ ['a'..'g']
содержит в общей сложности 448
возникновения 7
различные переменные.
Я бы рекомендовал использовать решатель SAT/SMT для проверки эквивалентности. В общем, такие проверки могут быть очень дорогими (NP-complete), и любой вид перевода в нормальные формы может вызвать экспоненциальный взрыв в представлении. Решатели SAT/SMT имеют пользовательские алгоритмы для решения таких проблем, и, возможно, лучше всего использовать такой решатель. Было бы тривиально перевести два экземпляра Property
и спросите, одинаковы ли они во всех назначениях переменных. Библиотека SBV (https://hackage.haskell.org/package/sbv) может использоваться для перевода с высокого уровня Haskell. Ответ на следующий вопрос содержит некоторые подробности о том, как это сделать:SAT решение с библиотекой Haskell SBV: как создать предикат из проанализированной строки?
если вы хотите отразить ассоциативность логических связок и (&&
) и или, используйте ассоциативную структуру данных, например list:
data Property a = And [Property a] | ...
если вы хотите коммутативность (A && B == B && A
), иди с данные.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
+-----------+-----------+----------+----------
страница 51 из лекция Гая Стила.