einfache Funktionen
|
- Knoten testen
- Teile eines Knotens selektieren
- Knoten oder Teile verändern
|
zusammengesetzte Funktionen
|
- ganze Teilbäume testen
- Teilbäume selektieren
- Teilbäume verändern
|
Kombination
|
von beliebigen Tests, Selektionen, Manipulationen
|
|
eingebettete problemspezifische Sprache
|
|
DSL, Domain Specific Language, Kombinatoren-Bibliothek
|
|
nur möglich bei einheitlichen Funktionstypen
|
| |
typische Signaturen
|
test :: ... -> XmlTree -> Bool
sel :: ... -> XmlTree -> String
child :: ... -> XmlTree -> Maybe Tree
edit :: ... -> XmlTree -> XmlTree
search :: ... -> XmlTree -> [XmlTree]
|
|
Wie soll mit Funktionen umgegangen werden, die nur auf einer bestimmten Knoten-Variante definiert sind,
z.B. Selektion des Elementnamens bei Textknoten?
|
| |
Lösung
|
ein Typ für alle Aufgaben
|
Filter
|
type XmlFilter = XmlTree -> [XmlTree]
|
|
Resultat ist eine Liste von Bäumen
|
Resultate |
|
Prädikate |
|
Selektion |
|
Editieren |
|
Löschen |
|
Suchen |
|
allgemein |
|
| |
einfache allgemeine Filter
|
|
none
|
0-Filter
|
|
none :: NTree node -> [NTree node]
none t = []
|
this
|
Identität
|
|
this :: NTree node -> [NTree node]
this t = [t]
|
allgemeiner Test
|
|
|
test :: (node -> Bool) ->
NTree node -> [NTree node]
test p t@(NTree n cs)
| p n = [t]
| otherwise = []
test p t@(NTree n cs)
= if p n
then this
else none
$ t
|
children
|
alle Kinder, eine Selektor-Funktion
|
|
children :: NTree node -> [NTree node]
children (NTree n cs) = cs
|
edit
|
edit :: (node -> node) ->
NTree node -> [NTree node]
edit f (NTree n cs) = [NTree (f n) cs]
|
| |
Filter Kombinatoren
|
|
Filter Typ
|
viele sinnvolle Filter können allgemein und ohne Ausnutzung der Struktur der Knoten-Attribute entwickelt werden
|
|
type TreeFilter n = NTree n -> [NTree n]
|
Komposition
|
2 Filter hintereinander anwenden
|
|
(>>>) :: TreeFilter n -> TreeFilter n ->
TreeFilter n
f1 >>> f2 = \t -> concat [f2 t1 | t1 <- f1 t]
f1 >>> f2 = concat . (map f2) . f1
f1 >>> f2 = concatMap f2 . f1
p1 >>> p2
grandChildren = children >>> children
|
|
Funktionskomposition, nur in Leserichtung von links nach rechts
|
Vereinigung
|
2 Filter "gleichzeitig" auf einen Baum anwenden
|
|
(<+>) :: TreeFilter n -> TreeFilter n ->
TreeFilter n
f1 <+> f2 = \t -> f1 t ++ f2 t
p1 <+> p2
|
Auswahl
entweder - oder
|
orElse :: TreeFilter n -> TreeFilter n ->
TreeFilter n
f `orElse` g
= \ t -> let res = f t
in if null res
then g t
else res
|
Verzweigung
|
if auf Filter angehoben
|
|
iff :: TreeFilter n -> TreeFilter n ->
TreeFilter n ->
TreeFilter n
iff p f g
= \ t -> if (not . null . p) t
then f t
else g t
|
Verzweigung
|
einfache Varianten: when und guards
|
|
when :: TreeFilter n -> TreeFilter n ->
TreeFilter n
f `when` p
= iff p f this
guards :: TreeFilter n -> TreeFilter n ->
TreeFilter n
p `guards` f
= iff p f none
|
Suchen
|
top down aller Teilbäume mit einer Eigenschaft
|
|
deep :: TreeFilter n ->
TreeFilter n
deep f = f
`orElse`
( getChildren >>> deep f )
deep isTableElem
|
Suchen
|
aller Teilbäume mit einer Eigenschaft
|
|
multi :: TreeFilter n ->
TreeFilter n
multi f = f
<+>
( getChildren >>> multi f )
multi isTableElem
|
Modifizieren
|
aller Kindknoten
|
|
processChildren :: TreeFilter n ->
TreeFilter n
processChildren f (NTree n cs)
= [NTree n (concatMap f cs)]
|
Modifizieren
|
aller Knoten (top down)
|
|
processTopDown :: TreeFilter n ->
TreeFilter n
processTopDown f t
= f
>>> processChildren
( processTopDown f)
|
URL umschreiben
|
rewriteURL :: XmlFilter
rewriteURL
= processTopDown
( rewriteHref
`when`
( hasName "a"
>>> hasAttr "href" ))
where
rewriteHref = ...
|
Anfragesprache
|
durch Kombination von Prädikaten und Baumtraversierung
|
Beispiel
|
Der Text aller H1-Überschriften in einem HTML-Dokument
|
|
hasName "html"
>>> getChildren
>>> hasName "body"
>>> getChildren
>>> deep (hasName "h1")
>>> getChildren
>>> deep isText
|
Beispiel
|
XPath-Anfragen: x/y/z//u/v
|
|
hasName "x"
>>> getChildren >>> hasName "y"
>>> getChildren >>> hasName "z"
>>> getChildren >>> deep (hasName "u")
>>> getChildren >>> hasName "v"
|
|
(/>) :: TreeFilter n -> TreeFilter n ->
TreeFilter n
f /> g = f >>> getChildren >>> g
hasName "x"
/> hasName "y"
/> hasName "z"
/> deep (hasName "u")
/> hasName "v"
|
DSL
|
Domain Specific Language (problemspezifische Sprache
eingebettet in Haskell
|
|
bewährte Technik
|
|
flexibel, effizient
|
|
1-1-Korrespondenz: Modell des Problembereichs <--> Programm
|
Voraussetzung
|
Konstruktion von eigenene problemspezifischen Kontrollstrunkturen
|