PPI - Teil 1 - Haskell

Werbung
PPI - Teil 1 - Haskell - Dokumentation
Michael Baron, Nils Oberhauser
11. Mai 2005
1
Phasen der Entwicklung
1.1
Erstes Treffen (Do, 14. April 2005)
• Vorbesprechung für das Praktikum Praktische Informatik.
• Austeilung der Praktikumsanleitung.
• Grobe Gruppeneinteilung.
1.2
Der Anfang (Do, 21. April 2005)
• Einteilung in Untergruppen
• Übernahme des Parsers für arithmetische Ausdrücke (TaschenrechnerBsp.) (10:00-12:00) (Source siehe Praktikumsanleitung)
• Hinzufügen einer Funktion zum Ausrechnen arithmetischer Syntaxbäume
calculate (12:00-13:00) (Source calculator.y), welche mittels PatternMatching realisiert wurde.
calculate :: Expr -> Int
calculate (Plus (Number n1) (Number n2)) = n1
calculate (Minus (Number n1) (Number n2)) = n1
calculate (Times (Number n1) (Number n2)) = n1
-- calculate (Div
(Number n1) (Number n2)) =
+ n2
- n2
* n2
n1 / n2
calculate (Plus e1 (Number n2)) = (calculate e1) + n2
calculate (Minus e1 (Number n2)) = (calculate e1) - n2
calculate (Times e1 (Number n2)) = (calculate e1) * n2
1
-- calculate (Div
e1 (Number n2)) = (calculate e1) / n2
calculate (Plus (Number n1) e2) = n1
calculate (Minus (Number n1) e2) = n1
calculate (Times (Number n1) e2) = n1
-- calculate (Div
(Number n1) e2) =
+ (calculate e2)
- (calculate e2)
* (calculate e2)
n1 / (calculate e2)
Auskommentierung der Division, da Typunverträglichkeit. Drei verschiedene
Versionen für Basiswerte und Mischungen aus Basiswert und Unterausdruck.
Beispiel-Aufruf
*Calc> calculate (parser (lexer "(1+2)*3"))
9
Aber zum Beispiel keine Prioritäten-Definition der einzelnen Operatoren. So
liefert folgende Rechnung ein falsches Ergebnis:
*Calc> calculate (parser (lexer "1+2*3"))
9
1.3
Rechner für konstante Bool’sche Ausdrücke (So,
24. April 2005)
• Anpassung des Taschenrechners an konstante Bool’sche Ausdrücke.
(16:30-18:00) (Source ProofEngine-mib.y)
• Neudefinition der einzelnen Token (logische Einheiten einer Formel)
%name parser
%tokentype { Token }
%token
Bool { TokenBool $$ }
’&’
{ TokenAnd }
’|’
{ TokenOr }
’=>’ { TokenImpl }
’<=>’ { TokenEqui }
’-’
{ TokenNot }
’(’
{ TokenOB }
’)’
{ TokenCB }
• Einführung einer (letztlich falschen) Rangfolge logischer Operatoren
2
%nonassoc
%right
%left
%nonassoc
%%
’<=>’
’=>’
’&’ ’|’
’-’
• Übersetzung der Backus-Naur-Form eines logischen Ausdrucks in HappyParser-Deklaration.
Formula :: { Formula }
Formula : Formula ’&’
Formula
| Formula ’|’
Formula
| Formula ’=>’ Formula
| Formula ’<=>’ Formula
| ’-’ Formula
| ’(’ Formula ’)’
| Bool
{
{
{
{
{
{
{
And
Or
Impl
Equi
Not
$1
$1
$1
$1
$2
$2
Bool $1
$3
$3
$3
$3
}
}
}
}
}
}
}
• Einführung der Haskell-Datentypen Token und Formula. (siehe Quellcode)
• Anpassung des Lexers
-- Lexer
lexer :: String -> [Token]
lexer
lexer
lexer
lexer
lexer
lexer
lexer
lexer
lexer
lexer
[] = []
(’&’:xs) =
TokenAnd
(’|’:xs) =
TokenOr
(’=’:(’>’:xs)) =
TokenImpl
(’<’:(’=’:(’>’:xs))) = TokenEqui
(’-’:xs) =
TokenNot
(’T’:xs) =
TokenBool True
(’F’:xs) =
TokenBool False
(’(’:xs) =
TokenOB
(’)’:xs) =
TokenCB
lexer (x:xs)
| isSpace x = lexer xs
| otherwise = error "parse error"
3
:
:
:
:
:
:
:
:
:
(lexer
(lexer
(lexer
(lexer
(lexer
(lexer
(lexer
(lexer
(lexer
xs)
xs)
xs)
xs)
xs)
xs)
xs)
xs)
xs)
• Deklaration extensionaler logischer Aussageoperationen (Implikation,
Äquivalenz).
-- Implikation, Aequivalenz
impl :: Bool -> Bool -> Bool
equi :: Bool -> Bool -> Bool
impl
impl
impl
impl
False
False
True
True
False
True
True
False
=
=
=
=
True
True
True
False
equi
equi
equi
equi
False
False
True
True
False
True
True
False
=
=
=
=
True
False
True
False
• und zu guter Letzt: Defintion einer calc-Funktion für logische Syntaxbäume mittels Pattern-Matching.
calc :: Formula -> Bool
calc
calc
calc
calc
(And
(And
(And
(And
(Bool
(
(Bool
(
b1)
f1)
b1)
f1)
(Bool
(Bool
(
(
b2))
b2))
f2))
f2))
=
=
=
=
b1
&&
(calc f1) &&
b1
&&
(calc f1) &&
b2
b2
(calc f2)
(calc f2)
calc
calc
calc
calc
(Or
(Or
(Or
(Or
(Bool
(
(Bool
(
b1)
f1)
b1)
f1)
(Bool
(Bool
(
(
b2))
b2))
f2))
f2))
=
=
=
=
b1
||
(calc f1) ||
b1
||
(calc f1) ||
b2
b2
(calc f2)
(calc f2)
calc
calc
calc
calc
(Impl
(Impl
(Impl
(Impl
(Bool
(
(Bool
(
b1)
f1)
b1)
f1)
(Bool
(Bool
(
(
b2))
b2))
f2))
f2))
=
=
=
=
impl
impl
impl
impl
b1
(calc f1)
b1
(calc f1)
b2
b2
(calc f2)
(calc f2)
calc
calc
calc
calc
(Equi
(Equi
(Equi
(Equi
(Bool
(
(Bool
(
b1)
f1)
b1)
f1)
(Bool
(Bool
(
(
b2))
b2))
f2))
f2))
=
=
=
=
equi
equi
equi
equi
b1
(calc f1)
b1
(calc f1)
b2
b2
(calc f2)
(calc f2)
4
calc (Not
calc (Not
(Bool
(
b1)) = not b1
f1)) = not (calc f1)
calculate f = (calc (parser (lexer f)))
Aufrufbeispiel
*ProofEngine> calculate "T|F=>F&-F"
False
Da die Operatorenrangfolge falsch ist, muss dieses Ergebnis natürlich auch
nicht stimmen. (T steht für Wahr, F für Falsch)
1.4
Es wird variabel (Di, 26. April 2005)
• Implementierung der Variablenunterstützung (20:00-21:15)
• Minimale Änderungen an Parser- und Typdeklaration zur Variablenunterstützung. (Source ProofEngine-mib2.y)
• Minimale Änderung des Lexers zum Lexen von alphabetischen Zeichenketten
• Kleinere Veränderung der calc-Funktion zur Unterstütztung einer AssignmentListe
• Definition einer Dictionary-Funktion zum Extrahieren von VariablenWerten aus einem Assignment.
value :: Assignment -> Variable -> Bool
value ((x,y):zs) v
| x == v = y
| otherwise = value zs v
1.5
Suchen nach Variablen (Do, 28. April 2005)
• Definition einer Funktion zur Extraktion von Variablen aus einer Formel. (vs bzw. später als varscan bezeichnet)
5
-- Variable Scanning
vs :: Formula -> [Variable]
vs
vs
vs
vs
(And
(And
(And
(And
(Var
(
(Var
(
b1)
f1)
b1)
f1)
(Var
(Var
(
(
b2))
b2))
f2))
f2))
=
=
=
=
[b1]
(vs f1)
[b1]
(vs f1)
++
++
++
++
[b2]
[b2]
(vs f2)
(vs f2)
vs
vs
vs
vs
(Or
(Or
(Or
(Or
(Var
(
(Var
(
b1)
f1)
b1)
f1)
(Var
(Var
(
(
b2))
b2))
f2))
f2))
=
=
=
=
[b1]
(vs f1)
[b1]
(vs f1)
++
++
++
++
[b2]
[b2]
(vs f2)
(vs f2)
vs
vs
vs
vs
(Impl
(Impl
(Impl
(Impl
(Var
(
(Var
(
b1)
f1)
b1)
f1)
(Var
(Var
(
(
b2))
b2))
f2))
f2))
=
=
=
=
[b1]
(vs f1)
[b1]
(vs f1)
++
++
++
++
[b2]
[b2]
(vs f2)
(vs f2)
vs
vs
vs
vs
(Equi
(Equi
(Equi
(Equi
(Var
(
(Var
(
b1)
f1)
b1)
f1)
(Var
(Var
(
(
b2))
b2))
f2))
f2))
=
=
=
=
[b1]
(vs f1)
[b1]
(vs f1)
++
++
++
++
[b2]
[b2]
(vs f2)
(vs f2)
vs
vs
vs
(Neg
(Neg
(Var
(
(Var
b1)) = [b1]
f1)) = (vs f1)
b1) = [b1]
• Elimination mehrfacher gleicher Einträge (einer sortierten Liste, wie
sich später herausstellen sollte :-))
-- elimination of multiple list elems
eliminate :: [Variable] -> [Variable]
eliminate [] = []
eliminate (x:[])
= [x]
eliminate (x:(y:zs)) = if x == y then (eliminate (x:zs))
else (x:(eliminate (y:zs)))
• Zusammenbau einer Assignmentliste alller möglichen Assignments für
eine Liste aller Variablen (so in etwa wie die Berechnung von Potenzmengen realisiert)...
6
posA :: [Variable] -> [Assignment]
posA []
= [[]]
posA (v:vs) = [ ((v,True):a) | a <- (posA vs) ]
++ [ ((v,False):a) | a <- (posA vs) ]
• ...und die Berechnung aller zugehöriger Ausdrucks-Werte
calca :: [Assignment] -> Formula -> [Bool]
calca a f = [ (calc n f) | n <- a ]
calct f = calca (posA (getVarsFromString f)) (parser (lexer f))
• Definition nützlicher Hilfsfunktionen (getVars bzw. getVarsFromString)
-- get Vars from Formula Tree/ Formula String
getVars :: Formula -> [Variable]
getVars f =
eliminate (sort (vs f))
getVarsFromString :: String -> [Variable]
getVarsFromString s = getVars (parser (lexer s))
So, dies dauerte von 10:00-12:00 (wie immer inkl. diverser Tests) und lieferte
die Datei ProofEngine-mibnio.y. (diese verwendet nun das vorgeschriebene
Typenmodul TypeDefs.lhs) Beispiel-Aufruf:
*ProofEngine> getVarsFromString "A=>B<=>B=>A"
["A","B"]
Selbstverständlich funktioniert dies auch mit längeren Variablennamen
*ProofEngine> getVarsFromString "Dies=>ist<=>ein=>Test"
["Dies","ist","ein","Test"]
und nun zu posA (später buildAss), welches eben die Assignments bauen
sollte
*ProofEngine> posA (getVarsFromString "A=>B<=>B=>A")
[[("A",True),("B",True)],[("A",True),("B",False)],[("A",False),("B",True)],
[("A",False),("B",False)]]
7
1.6
und zu guter Letzt: Das Finish (So, 01. Mai 2005)
• Der langersehnte Zusammenschluss aller Elementarfunktionen zu einem
Ganzen plus die Definition der Schnittstellenfunktionen proofEngine
(wie verlangt), isTautology, isContradiction, sowie isSatisfiable, welches
allerdings nicht Bool, denn dies wäre trivial, sondern die Liste aller
möglichen Satisfiabilities liefert. (Satisfiabilities erschien uns nämlich
auf Grund typographischer Probleme nicht Quellcode-fähig. :-))
------
ProofEngine.y
Michael Baron, Nils Oberhauser
{
module ProofEngine where
import TypeDefs
import Char
import List
data Token =
|
|
|
|
|
|
|
TokenVar Variable
TokenAnd
TokenOr
TokenImpl
TokenEqui
TokenNeg
TokenOB
TokenCB
proofEngine
:: String
-> Result
buildAss
calc
calcStr
calcStrFilter
elimMult
getVars
getVarsStr
happyError
::
::
::
::
::
::
::
::
->
->
->
->
->
->
->
->
[Variable]
Assignment
Assignment
String
[Variable]
Formula
String
[Token]
8
[Assignment]
Formula
-> Bool
String
-> Bool
Assignment
-> Bool
[Variable]
[Variable]
[Variable]
a
isTautology
isContradiction
isSatisfiable
lexer
lexAlpha
listTrue
listFalse
logEqui
logImpl
value
varscan
}
::
::
::
::
::
::
::
::
::
::
::
String
String
String
String
String
[Bool]
[Bool]
Bool
Bool
Assignment
Formula
->
->
->
->
->
->
->
->
->
->
->
Bool
Bool
[Assignment]
[Token]
[Token]
Bool
Bool
Bool
-> Bool
Bool
-> Bool
Variable
-> Bool
[Variable]
%name parser
%tokentype { Token }
%token
’<=>’ { TokenEqui }
’=>’ { TokenImpl }
’&’
{ TokenAnd }
’|’
{ TokenOr }
’-’
{ TokenNeg }
’(’
{ TokenOB }
’)’
{ TokenCB }
Var
{ TokenVar $$ }
%nonassoc
%right
%left
%left
%nonassoc
%%
’<=>’
’=>’
’&’
’|’
’-’
Formula :: { Formula }
Formula : Formula ’<=>’
| Formula ’=>’
| Formula ’&’
| Formula ’|’
|
’-’
|
’(’
| Var
Formula
Formula
Formula
Formula
Formula
Formula ’)’
9
{
{
{
{
{
{
{
Equi
Impl
And
Or
Neg
Var
$1
$1
$1
$1
$2
$2
$1
$3
$3
$3
$3
}
}
}
}
}
}
}
{
proofEngine s
| isTautology s
= Tautology
| isContradiction s = Unsatisfiable
| otherwise
= Satisfiable (isSatisfiable s)
-buildAss []
= [[]]
buildAss (v:vs) = [ ((v,True):a) | a <- (buildAss vs) ]
++ [ ((v,False):a) | a <- (buildAss vs) ]
-calc
calc
calc
calc
a
a
a
a
(And
(And
(And
(And
(Var
(
(Var
(
b1)
f1)
b1)
f1)
(Var
(Var
(
(
b2))
b2))
f2))
f2))
=
=
=
=
(value
(calc
(value
(calc
a
a
a
a
b1)
f1)
b1)
f1)
&&
&&
&&
&&
(value
(value
(calc
(calc
a
a
a
a
b2)
b2)
f2)
f2)
calc
calc
calc
calc
a
a
a
a
(Or
(Or
(Or
(Or
(Var
(
(Var
(
b1)
f1)
b1)
f1)
(Var
(Var
(
(
b2))
b2))
f2))
f2))
=
=
=
=
(value
(calc
(value
(calc
a
a
a
a
b1)
f1)
b1)
f1)
||
||
||
||
(value
(value
(calc
(calc
a
a
a
a
b2)
b2)
f2)
f2)
calc
calc
calc
calc
a
a
a
a
(Impl
(Impl
(Impl
(Impl
(Var
(
(Var
(
b1)
f1)
b1)
f1)
(Var
(Var
(
(
b2))
b2))
f2))
f2))
=
=
=
=
logImpl
logImpl
logImpl
logImpl
(value
(calc
(value
(calc
a
a
a
a
b1)
f1)
b1)
f1)
(value
(value
(calc
(calc
a
a
a
a
b2)
b2)
f2)
f2)
calc
calc
calc
calc
a
a
a
a
(Equi
(Equi
(Equi
(Equi
(Var
(
(Var
(
b1)
f1)
b1)
f1)
(Var
(Var
(
(
b2))
b2))
f2))
f2))
=
=
=
=
logEqui
logEqui
logEqui
logEqui
(value
(calc
(value
(calc
a
a
a
a
b1)
f1)
b1)
f1)
(value
(value
(calc
(calc
a
a
a
a
b2)
b2)
f2)
f2)
(Var
(
(Var
b1))
f1))
b1)
calc a (Neg
calc a (Neg
calc a
= not (value a b1)
= not (calc a f1)
=
(value a b1)
-calcStr a f = (calc a (parser (lexer f)))
10
-calcStrFilter f a = (calc a (parser (lexer f)))
-elimMult []
= []
elimMult (x:[])
= [x]
elimMult (x:(y:zs)) = if x == y then (elimMult (x:zs))
else (x:(elimMult (y:zs)))
-getVars f =
elimMult (sort (varscan f))
-getVarsStr s = getVars (parser (lexer s))
-happyError _ = error "(EE) ProofEngine :: parse error!"
-isTautology s = listTrue [ (calc (ass) (parser (lexer s)))
| ass <- (buildAss (getVarsStr s)) ]
-isContradiction s = not (listFalse [ (calc (ass) (parser (lexer s)))
| ass <- (buildAss (getVarsStr s)) ])
-isSatisfiable s = (filter (calcStrFilter s) (buildAss (getVarsStr s)))
-lexer [] = []
11
lexer
lexer
lexer
lexer
lexer
lexer
lexer
(’<’:(’=’:(’>’:xs)))
(’=’:(’>’:xs))
(’&’:xs)
(’|’:xs)
(’-’:xs)
(’(’:xs)
(’)’:xs)
=
=
=
=
=
=
=
TokenEqui
TokenImpl
TokenAnd
TokenOr
TokenNeg
TokenOB
TokenCB
:
:
:
:
:
:
:
(lexer
(lexer
(lexer
(lexer
(lexer
(lexer
(lexer
lexer (x:xs)
| isSpace x = lexer xs
| isAlpha x = lexAlpha (x:xs)
| otherwise = error "parse error"
-lexAlpha (x:xs) = TokenVar alpha : (lexer s)
where [(alpha,s)] = lex (x:xs)
-listTrue []
= True
listTrue (x:xs) = x && (listTrue xs)
-listFalse []
= False
listFalse (x:xs) = x || (listFalse xs)
-logEqui
logEqui
logEqui
logEqui
False
False
True
True
False
True
True
False
=
=
=
=
True
False
True
False
False
False
True
True
False
True
True
False
=
=
=
=
True
True
True
False
-logImpl
logImpl
logImpl
logImpl
12
xs)
xs)
xs)
xs)
xs)
xs)
xs)
-value ((x,y):zs) v
| x == v = y
| otherwise = value zs v
-varscan
varscan
varscan
varscan
(And
(And
(And
(And
(Var
(
(Var
(
b1)
f1)
b1)
f1)
(Var
(Var
(
(
b2))
b2))
f2))
f2))
=
=
=
=
[b1]
++ [b2]
(varscan f1) ++ [b2]
[b1]
++ (varscan f2)
(varscan f1) ++ (varscan f2)
varscan
varscan
varscan
varscan
(Or
(Or
(Or
(Or
(Var
(
(Var
(
b1)
f1)
b1)
f1)
(Var
(Var
(
(
b2))
b2))
f2))
f2))
=
=
=
=
[b1]
++ [b2]
(varscan f1) ++ [b2]
[b1]
++ (varscan f2)
(varscan f1) ++ (varscan f2)
varscan
varscan
varscan
varscan
(Impl
(Impl
(Impl
(Impl
(Var
(
(Var
(
b1)
f1)
b1)
f1)
(Var
(Var
(
(
b2))
b2))
f2))
f2))
=
=
=
=
[b1]
++ [b2]
(varscan f1) ++ [b2]
[b1]
++ (varscan f2)
(varscan f1) ++ (varscan f2)
varscan
varscan
varscan
varscan
(Equi
(Equi
(Equi
(Equi
(Var
(
(Var
(
b1)
f1)
b1)
f1)
(Var
(Var
(
(
b2))
b2))
f2))
f2))
=
=
=
=
[b1]
++ [b2]
(varscan f1) ++ [b2]
[b1]
++ (varscan f2)
(varscan f1) ++ (varscan f2)
varscan
varscan
varscan
(Neg
(Neg
(Var
(
(Var
b1))
f1))
b1)
= [b1]
= (varscan f1)
= [b1]
}
Da nun aber der Ruf nach Dokumentation immer lauter wurde, dokumentierten wir mit Hilfe von Haddock einen Großteil der Funktionen (siehe ProofEngine.html) und schrieben eine PDF-Dokumentation welche noch einmal zumindest essentielle Überlegungen darstellen soll.
13
2
und nun zur Funktionsweise
Vielleicht zuerst die Praxis:
...
*ProofEngine> proofEngine "A=>A"
Tautology
*ProofEngine> proofEngine "A=>-A"
Satisfiable [[("A",False)]]
*ProofEngine> proofEngine "A&-A"
Unsatisfiable
*ProofEngine> proofEngine "A=>A&-A"
Satisfiable [[("A",False)]]
*ProofEngine> proofEngine "A=>A&(-A|B)"
Satisfiable [[("A",True),("B",True)],[("A",False),("B",True)],
[("A",False),("B",False)]]
...
14
Herunterladen