Создание таблиц истинности для логических выражений в Haskell

Первая часть - это функция оценки, имеющая следующую сигнатуру типа:

evaluate :: Logic Expr -> [(Variable, Bool)] -> Bool

Он принимает логическое выражение и список пар присваивания в качестве входных данных и возвращает значение выражения в соответствии с предоставленным логическим присваиванием. Список назначений - это отдельный список пар, в котором каждая пара содержит переменную и ее логическое присвоение. То есть, если вы передадите в функцию выражение A B и присвоение A = 1 и B = 0, ваша функция должна вернуть 0 (это происходит из Digital Logic Design, 0 соответствует false, а 1 соответствует true).

Вот что мне удалось сделать до сих пор:

type Variable =  Char

data LogicExpr = V Variable
                 | Negation  LogicExpr
                 | Conjunction LogicExpr LogicExpr
                 | Disjunction  LogicExpr LogicExpr 
                 | Implication  LogicExpr LogicExpr 


evaluate :: LogicExpr -> [(Variable,Bool)] -> Bool

evaluate (V a) ((x1,x2):xs) | a==x1 = x2
                            | otherwise = (evaluate(V a)xs)

evaluate (Negation a) l | (evaluate a l)==True = False
                        | otherwise = True

evaluate (Conjunction a b) l = (evaluate a l)&&(evaluate b l)

evaluate (Disjunction a b) l = (evaluate a l)||(evaluate b l)

evaluate (Implication a b) l
    | (((evaluate b l)==False)&&((evaluate a l)==True)) = False
    | otherwise = True

Следующая часть - определить generateTruthTable, функцию, которая принимает логическое выражение в качестве входных данных и возвращает таблицу истинности выражения в виде списка списков пар присваиваний. То есть, если вы передадите в функцию выражение E = A B, ваша функция должна вернуть A = 0, B = 0, E = 0 | A = 0, B = 1, E = 0 | A = 1, B = 0, E = 0 | А = 1, В = 1, Е = 1.

Я не совсем знаком с синтаксисом, поэтому не знаю, как вернуть список.


person Community    schedule 05.06.2009    source источник
comment
ИМХО, намного больше людей будут смотреть на это, если убрать [срочно].   -  person Lucas Jones    schedule 05.06.2009
comment
Когда я учился в колледже, моим лучшим другом был карманный бегун. Никогда не забывала о домашних заданиях, расставленных по ближайшему сроку (они говорят, что это наиболее эффективно).   -  person Jason Catena    schedule 05.06.2009
comment
пожалуйста, не обращайте внимания на грубые плакаты. Это не представитель типичного опыта.   -  person Ethan Heilman    schedule 05.06.2009
comment
Что ж, предоставлять тем, кто задает домашнее задание, частичные ответы, которые являются не более чем намеками, и заставлять OP выполнять большую часть работы - это довольно нормально для SO - или на любом форуме, связанном с образованием, на самом деле. Грубость ответов бывает разной, но вы должны научиться иметь толстую кожу практически на любом форуме в Интернете. Не стоит слишком волноваться только потому, что люди выражают резкое несогласие.   -  person ephemient    schedule 06.06.2009
comment
жаль, что нет способа пометить ответы (не сообщения) как не связанные с программированием или как субъективные и аргументированные :)   -  person Jimmy    schedule 06.06.2009
comment
Вы изучаете 4 языка и меньше всего обращаете внимание на самый крутой язык в списке. : \   -  person Rayne    schedule 06.06.2009


Ответы (2)


Стандартные библиотечные функции, повторное использование кода. Кроме того, использование круглых скобок и интервалы действительно испорчены.

evaluate (V a) l =
    case lookup a l
      of Just x -> x
         Nothing -> error $ "Unbound variable: " ++ show a
-- same as
evaluate (V a) l = maybe (error $ "Unbound variable: " ++ show a) id $ lookup a l

evaluate (Negation a) l = not $ evaluate a l

evaluate (Implication a b) l = evaluate (Negation a `Disjunction` b) l

Теперь вы хотите generateTruthTable? Это легко, просто возьмите все возможные состояния логических переменных и прикрепите вычисленное выражение к концу каждой.

generateTruthTable :: [Variable] -> LogicExpr -> [[(Variable, Bool)]]
generateTruthTable vs e = [l ++ [('E', evaluate e l)] | l <- allPossible vs]

Если бы только у вас была функция для генерации всех этих возможных состояний.

allPossible :: [Variable] -> [[(Variable, Bool)]]

Следуя моему функциональному инстинкту, я чувствую, что это должен быть катаморфизм. В конце концов, ему нужно просмотреть все в списке, но вернуть что-то с другой структурой, и его, вероятно, можно разбить простым способом, потому что это класс CS начального уровня. (Меня не волнует номер курса, это вводный материал.)

allPossible = foldr step initial where
    step v ls = ???; initial = ???

Теперь foldr :: (a -> b -> b) -> b -> [a] -> b, поэтому первые два параметра должны быть step :: a -> b -> b и initial :: b. Теперь allPossible :: [Variable] -> [[(Variable, Bool)]] = foldr step initial :: [a] -> b. Хм, это должно означать, что a = Variable и b = [[(Variable, Bool)]]. Что это означает для step и initial?

    step :: Variable -> [[(Variable, Bool)]] -> [[(Variable, Bool)]]
    initial :: [[(Variable, Bool)]]

Интересно. Каким-то образом должен быть способ step из списка состояний переменных и добавить к нему одну переменную и некоторый initial список без переменных вообще.

Если ваш разум уже сумел «щелкнуть» по парадигме функционального программирования, этого должно быть более чем достаточно. В противном случае вы в значительной степени облажались через пару часов, когда должно быть задание, независимо от того, какие инструкции вы здесь получили. Удачи, и если вы все еще застряли после срока выполнения задания, вам следует спросить своего профессора или задать несрочный вопрос здесь.


Если у вас возникли основные проблемы с удобством использования языка («каков синтаксис», «какова семантика времени выполнения», «есть ли ранее существовавшие функции для xxx» и т. Д.) :

  • Язык и библиотеки Haskell 98 - это свободно доступное каноническое определение базового языка и библиотек. Дополнительные ссылки доступны в вики-странице Haskell.
  • Для языковых расширений после 98 см. GHC документация.
  • GHC, Hugs и другие современные реализации Haskell также предоставляют гораздо более богатую стандартную библиотеку, чем указано в Haskell 98. Полная документация для иерархические библиотеки также доступны в Интернете.
  • Hooge - это специализированная поисковая машина для расширенных стандартных библиотек Haskell. Hayoo! аналогичен, но также охватывает HackageDB, коллекция библиотек Haskell, выходящая далеко за рамки стандартного распространения.

Я надеюсь, что ваш класс предоставил аналогичные ресурсы, но если нет, все вышеперечисленное легко найти с помощью поиска Google.

При наличии надлежащих ссылок любой программист, достойный его или ее соли, должен иметь возможность понять синтаксис любого нового языка в течение нескольких часов и иметь рабочее представление о среде выполнения в течение нескольких дней. Конечно, освоение новой парадигмы может занять много времени, и несколько несправедливо заставлять учеников придерживаться тех же стандартов, но для этого и предназначен класс.

Вопросы о проблемах более высокого уровня в Stack Overflow могут потребовать меньше ответов, но они также будут предоставлены с гораздо меньшей раздражительностью :) Вопросы домашнего задания относятся к категории «делай мою работу за меня!» в глазах большинства людей.


Спойлер

Пожалуйста, не обманывай. Однако просто чтобы вы почувствовали, насколько потрясающие вещи можно делать в Haskell ...

{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
{-# LANGUAGE OverlappingInstances, PatternGuards #-}

module Expr (Ring(..), (=:>), Expr(..), vars, eval, evalAll) where

import Control.Monad.Error

infixl 5 =:>, :=>
infixl 6 +:, -:, :+, :-
infixl 7 *:, :*

class (Eq a) => Ring a where
    (+:) :: a -> a -> a; (-:) :: a -> a -> a; x -: y = x +: invert y
    (*:) :: a -> a -> a; invert :: a -> a; invert x = zero -: x
    zero :: a; one :: a
(=:>) :: (Ring a) => a -> a -> a
(=:>) = flip (-:)

instance (Num a) => Ring a where
    (+:) = (+); (-:) = (-); (*:) = (*)
    invert = negate; zero = 0; one = 1

instance Ring Bool where
    (+:) = (||); (*:) = (&&)
    invert = not; zero = False; one = True

data Expr a b
  = Expr a b :+ Expr a b | Expr a b :- Expr a b
  | Expr a b :* Expr a b | Expr a b :=> Expr a b
  | Invert (Expr a b) | Var a | Const b

paren :: ShowS -> ShowS
paren ss s = '(' : ss (')' : s)

instance (Show a, Show b) => Show (Expr a b) where
    showsPrec _ (Const c) = ('@':) . showsPrec 9 c
    showsPrec _ (Var v) = ('$':) . showsPrec 9 v
    showsPrec _ (Invert e) = ('!':) . showsPrec 9 e

    showsPrec n e@(a:=>b)
      | n > 5 = paren $ showsPrec 0 e
      | otherwise = showsPrec 7 a . ('=':) . ('>':) . showsPrec 5 b

    showsPrec n e@(a:*b)
      | n > 7 = paren $ showsPrec 0 e
      | otherwise = showsPrec 7 a . ('*':) . showsPrec 7 b

    showsPrec n e | n > 6 = paren $ showsPrec 0 e
    showsPrec _ (a:+b) = showsPrec 6 a . ('+':) . showsPrec 6 b
    showsPrec _ (a:-b) = showsPrec 6 a . ('-':) . showsPrec 6 b

vars :: (Eq a) => Expr a b -> [a]
vars (a:+b) = vars a ++ vars b
vars (a:-b) = vars a ++ vars b
vars (a:*b) = vars a ++ vars b
vars (a:=>b) = vars a ++ vars b
vars (Invert e) = vars e; vars (Var v) = [v]; vars _ = []

eval :: (Eq a, Show a, Ring b, Monad m) => [(a, b)] -> Expr a b -> m b
eval m (a:+b) = return (+:) `ap` eval m a `ap` eval m b
eval m (a:-b) = return (-:) `ap` eval m a `ap` eval m b
eval m (a:*b) = return (*:) `ap` eval m a `ap` eval m b
eval m (a:=>b) = return (=:>) `ap` eval m a `ap` eval m b
eval m (Invert e) = return invert `ap` eval m e
eval m (Var v)
  | Just c <- lookup v m = return c
  | otherwise = fail $ "Unbound variable: " ++ show v
eval _ (Const c) = return c

namedProduct :: [(a, [b])] -> [[(a, b)]]
namedProduct = foldr (\(v, cs) l -> concatMap (\c -> map ((v, c):) l) cs) [[]]

evalAll :: (Eq a, Show a, Ring b) => [b] -> a -> Expr a b -> [[(a, b)]]
evalAll range name e =
    [ vs ++ [(name, either error id $ eval vs e)]
    | vs <- namedProduct $ zip (vars e) (repeat range)
    ]
$ ghci
GHCi, version 6.10.2: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer ... linking ... done.
Loading package base ... linking ... done.
Prelude> :l Expr.hs
[1 of 1] Compiling Expr             ( Expr.hs, interpreted )
Ok, modules loaded: Expr.
*Expr> mapM_ print . evalAll [1..3] 'C' $ Var 'A' :* Var 'B'
Loading package mtl-1.1.0.2 ... linking ... done.
[('A',1),('B',1),('C',1)]
[('A',1),('B',2),('C',2)]
[('A',1),('B',3),('C',3)]
[('A',2),('B',1),('C',2)]
[('A',2),('B',2),('C',4)]
[('A',2),('B',3),('C',6)]
[('A',3),('B',1),('C',3)]
[('A',3),('B',2),('C',6)]
[('A',3),('B',3),('C',9)]
*Expr> let expr = Var 'A' :=> (Var 'B' :+ Var 'C') :* Var 'D'
*Expr> expr
$'A'=>($'B'+$'C')*$'D'
*Expr> mapM_ print $ evalAll [True, False] 'E' expr
[('A',True),('B',True),('C',True),('D',True),('E',True)]
[('A',True),('B',True),('C',True),('D',False),('E',False)]
[('A',True),('B',True),('C',False),('D',True),('E',True)]
[('A',True),('B',True),('C',False),('D',False),('E',False)]
[('A',True),('B',False),('C',True),('D',True),('E',True)]
[('A',True),('B',False),('C',True),('D',False),('E',False)]
[('A',True),('B',False),('C',False),('D',True),('E',False)]
[('A',True),('B',False),('C',False),('D',False),('E',False)]
[('A',False),('B',True),('C',True),('D',True),('E',True)]
[('A',False),('B',True),('C',True),('D',False),('E',True)]
[('A',False),('B',True),('C',False),('D',True),('E',True)]
[('A',False),('B',True),('C',False),('D',False),('E',True)]
[('A',False),('B',False),('C',True),('D',True),('E',True)]
[('A',False),('B',False),('C',True),('D',False),('E',True)]
[('A',False),('B',False),('C',False),('D',True),('E',True)]
[('A',False),('B',False),('C',False),('D',False),('E',True)]
person ephemient    schedule 05.06.2009

Базовый evaluate довольно прост:

import Data.Maybe (fromJust)
import Data.List (nub)

type Variable = Char
data LogicExpr
   = Var Variable
   | Neg LogicExpr
   | Conj LogicExpr LogicExpr
   | Disj LogicExpr LogicExpr
   | Impl LogicExpr LogicExpr
   deriving (Eq, Ord)

-- evaluates an expression
evaluate :: LogicExpr -> [(Variable, Bool)] -> Bool
evaluate (Var v) bs      = fromJust (lookup v bs)
evaluate (Neg e) bs      = not (evaluate e bs)
evaluate (Conj e1 e2) bs = evaluate e1 bs && evaluate e2 bs
evaluate (Disj e1 e2) bs = evaluate e1 bs || evaluate e2 bs
evaluate (Impl e1 e2) bs = not (evaluate e1 bs) || evaluate e2 bs

Чтобы создать таблицу истинности, вы сначала должны найти все переменные в выражении, а затем сгенерировать все возможные назначения для этих переменных. Значения истинности этих назначений можно легко определить с помощью уже реализованной функции evaluate:

-- get variables in an expression
varsp :: LogicExpr -> [Variable]
varsp (Var v)      = [v]
varsp (Neg e)      = varsp e
varsp (Conj e1 e2) = varsp e1 ++ varsp e2
varsp (Disj e1 e2) = varsp e1 ++ varsp e2
varsp (Impl e1 e2) = varsp e1 ++ varsp e2

-- get variables in an expression without duplicates
vars :: LogicExpr -> [Variable]
vars = nub . varsp

-- possible boolean values
bools = [True, False]

-- all possible combinations of variable assignments
booltable :: [Variable] -> [[(Variable, Bool)]]
booltable [] = [[]]
booltable (a:as) = [(a,b) : r | b <- bools, r <- booltable as]

-- variable assignments and corresponding evaluation of an expression
truthtable :: LogicExpr -> [([(Variable, Bool)], Bool)]
truthtable e = [(bs, evaluate e bs) | bs <- booltable (vars e)]

Если вы хотите исследовать темные уголки стандартной библиотеки, вы также можете написать экземпляр Read для удобного ввода LogicExprs:

-- read a right-associative infix operator
readInfix opprec constr repr prec r
   = readParen (prec > opprec)
     (\r -> [(constr e1 e2, u) |
             (e1,s) <- readsPrec (opprec+1) r,
             (op,t) <- lex s,
             op == repr,
             (e2,u) <- readsPrec (opprec) t]) r

instance Read LogicExpr where
   readsPrec prec r
      =  readInfix 1 Impl "->" prec r
      ++ readInfix 2 Disj "|" prec r
      ++ readInfix 3 Conj "&" prec r
      ++ readParen (prec > 4)
         (\r -> [(Neg e, t) |
                 ("!",s) <- lex r,
                 (e,t)   <- readsPrec 4 s]) r
      ++ readParen (prec > 5)
         (\r -> [(Var v, s) |
                 ([v], s) <- lex r]) r

А таблицы истинности можно красиво распечатать:

showcell :: (Variable, Bool) -> String
showcell (v,b) = v : "=" ++ show b

showrow :: [(Variable, Bool)] -> Bool -> String
showrow []     b = show b
showrow [a]    b = showcell a ++ " => " ++ show b
showrow (a:as) b = showcell a ++ " && " ++ showrow as b

printrow :: ([(Variable, Bool)], Bool) -> IO ()
printrow = putStrLn . uncurry showrow

printtbl :: [([(Variable, Bool)], Bool)] -> IO ()
printtbl = mapM_ printrow

Все вместе таблицы истинности могут быть сгенерированы следующим образом:

Prelude Main> printtbl $ truthtable $ read "(a -> b) & (b -> a)"
a=True && b=True => True
a=True && b=False => False
a=False && b=True => False
a=False && b=False => True

Prelude Main> printtbl $ truthtable $ read "(a | b) | (!a & !b)"
a=True && b=True => True
a=True && b=False => True
a=False && b=True => True
a=False && b=False => True
person sth    schedule 09.06.2009