• Keine Ergebnisse gefunden

Eigene Datentypen in Haskell

Haskell erlaubt benutzerdefinierte Datentypen. Diese können mit dem Schlüsselwort data und Datenkonstruktoren definiert werden. Konstruktoren werden mit großen Anfangs-buchstaben geschrieben.

Unsere CH-Sprache können wir dementsprechend als rekursiver Haskell Datentyp wie folgt definieren:

d a t a Lch = Var S t r i n g - - x = V a r i a b l e " x "

| L a m b d a S t r i n g Lch - - \ x . e = L a m b d a " x " e

| C o n s t r S t r i n g [Lch] - - C o n s t r " m y c o n s " x xs

| L e t r e c [B i n d] Lch d e r i v i n g(S h o w) - - l e t r e c " x " : = V a r " x " in V a r " z "

Listing 1: LcH-ADT

Rekursiv, deswegen weil Lch wieder auf der rechten Seite der Definition auftaucht.

Var, Lambda, Constr und Letrec sind Typkonstruktor der Sprache LcH (Datentyp).

• Der Konstruktor Var erwartet einen Argument vom Typ string, der für einen Variablennamen steht.

• Lambda hat zwei Argumente, der erste ist vom Typ String und der zweite ein body.

Der body kann ein beliebiges Argument vom Type Lch sein. String steht für einen Variablennamen der von Lambda gebunden wird und und in body gilt.

• Constr erwartet einen ersten Argument vom Typ String und eine Liste mit Para-metern vom Typ LcH. String steht für Operatorname.

• Letrec erwartet auch zwei Argumente und zwar eine Liste von Parametren vom Typ Bind [Bind] und ein body vom Typ Lch.

Die Bindungen werden als Datentyp folgendermaßen definiert:

d a t a B i n d = S t r i n g := Lch d e r i v i n g(S h o w)

Listing 2: Menge der Bindungen

• Ein Parameter vom Typ Bind, stellt eine Bindung dar und zwar wird einen Varia-blennamen vom Typ String ein Wert vom Typ Lch zugewiesen.

Für die Beschriftung von Knoten und Kanten definieren wir ebenso so einen Datentyp Label. Dabei sind LAMVAR, LETVAR, LETREC, LAMBDA, CONSTR und FreeVar-String die Konstruktoren und VAR , LVAR , BODY, BIND , IN, LAMBDA und Num Int Beschriftungen für Knoten.

d a t a L a b e l = VAR | L A M V A R | L V A R | B O D Y | L E T V A R | B I N D | L E T R E C | IN | L A M B D A

| N u m I n t | F r e e V a r S t r i n g | C O N S T R S t r i n g d e r i v i n g(S h o w)

Listing 3: Labels

Das Anhängen von deriving (Show) macht das definierte Datentyp zu Instanz der Klasse Show und dient dazu Werte des Typs anzeigen.

Eine Datenstruktur die wir in mehreren Stellen dieser Implementierung aus Effizienzgrün-den benutzen, ist die Wörterbuchdatenstruktur. Eine Datenstruktur für Wörterbücher ist bereits im ModulData.Mapvorhanden.Data.Mapimplementiert balancierte Suchbäu-me in Haskell. Eine ”Map“ ist eine Menge von (key-,value-)Paaren, so dass pro Schlüssel nur ein Paar vorkommt. Dies kann man auch als Funktion von der Menge der Schlüssel in die Menge der möglichen Daten verstehen (map), wobei ein fehlender Schlüssel so interpretiert wird, dass die Funktion dort undefiniert ist.

Beispiele für vordefinierte Funktionen auf Map-Objekten sind:

lookup findet ein Element zu gegebenem Schlüssel insert fügt ein Element ein.

delete löscht ein Element zu gegebenem Schlüssel.

update ändert ein Wert zu gegebenem Schlüssel

foldrWithKey faltet über die Map die Elemente (Schlüssel-, Wert-)Paar in abstei-gender Reihenfolge.

filterWithKey sortiert aus, mittels des Schlüssel.

Mit dem Befehl:

i m p o r t q u a l i f i e d D a t a.Map as Map

wird das Modul Data.Map importiert, um Namenskonflikte zu vermeiden mit Funktionen anderer Module, wird das Modul als „qualified“ importiert und alle seine Funktionen fangen mit Map. an.

• Knoten entsprechen Integer-Werte:

t y p e N o d e = I n t

• Die Nachfolger eines Knotens v werden durch Adjazenzlisten repräsentiert, in denen die einzelnen Kanten als Tupel repräsentiert werden, die ein Kantenlabel des Typs LABEL und die Knotennummer des Nachfolgers von v enthalten:

t y p e O u t E d g e s = [(Node, L a b e l)]

• Der Graph ist ein ”Map“. Er speichert zu jedem Knoten v sein Label und seine ausgehenden Kanten (v, ui) in Form eines Tupels, genauer gesagt, jeder Knoten v(key) wird zu einem Tupel (value) assoziiert. Das Tupel für Value enthält einen

Label des Knotens v und die Liste seiner Nachfolgernknoten die aus Tupeln (ui ,Label der Kante (v, ui)) bestehen.

t y p e G r a p h = Map.Map N o d e (Label, O u t E d g e s)

Nach dem wir vorgestellt haben, wie die Sprache Lch und der Graph in Haskell definiert sind, zeigen wir anhand wichtiger Auszüge aus dem Quellcode die Schritte zur algorith-mischen Lösung des Alpha-Äquivalenz-Problems für die Ausdrücke der Sprache Lch.

Da die DVC eines Lch- Ausdrucks nicht vorausgesetzt wird, speichern wir die freien und gebundenen Variablen jeweils in einen Map. Dies können wir in Haskell folgendermaßen definieren:

t y p e V a r s M a p = Map.Map S t r i n g N o d e t y p e B o u n d V a r s M a p = V a r s M a p

t y p e F r e e V a r s M a p = V a r s M a p

Dabei werden die Variablennamen zum Knotenindex assoziiert. Also (key ,Value) ent-spricht (String, Int).

„makeGraph“ ist eigentlich die Funktion, die einen Lch-Ausdruck in einen Graphen umwandeln soll. Sie startet aber nur den Job und ruft eine rekursive Funktion f auf.

f :: Lch - > G r a p h - > I n t - > B o u n d V a r s M a p - > F r e e V a r s M a p - >

(Node, Graph, Int, F r e e V a r s M a p)

Die Funktion f hat fünf Argumente. Ein Objekt vom Type Lch, ein zweites Argument vom Typ Graph, der am Anfang leer ist. Ein drittes Argument vom Typ Int ist eine Zählvariable, die bestimmt welche Nummer dem nächsten Knoten vergeben werden soll.

Sie fängt bei null an und bei jedem Erstellen eines neuen Knoten wird sie inkrementiert.

Die zwei letzten Argumente sind vom Typ VarsMap. Das Erste dient zur Speicherung von gebundenen Variablen und das Zweite dient zur Speicherung von freien Variablen.

Am Anfang sind sie beiden leer. Diese Argumente sind bis auf den zweiten (Graph) Hilfsvariablen, die nur für die rekursive Aufrufe von f wichtig sind. Deswegen werden bei der Rückgabe an der Hauptfunktion „makeGraph“ ignoriert.

Jetzt betrachten wir den Fall, dass der Lch-Ausdruck ein Lambda-Ausdruck ist mit der gebundenen Variable x und dem body, der wieder ein beliebiger Lch-Ausdruck ist.

f (L a m b d a x b o d y) g r a p h c o u n t e r b o u n d V a r s f r e e V a r s

= l e t (l a m b d a N o d e, c o u n t e r 2) = m a k e N e w N o d e c o u n t e r

(l a m V a r N o d e, graph2, c o u n t e r 3) = m a k e L e a f N o d e g r a p h L A M V A R c o u n t e r 2 n e w B o u n d V a r s = Map.i n s e r t x l a m V a r N o d e b o u n d V a r s

(b o d y N o d e, graph3, c o u n t e r 4, f r e e V a r s 2) = f b o d y g r a p h 2 c o u n t e r 3

n e w B o u n d V a r s f r e e V a r s g r a p h 4 = a d d N o d e g r a p h 3 l a m b d a N o d e L A M B D A [(l a m V a r N o d e, L V A R) ,

(b o d y N o d e, B O D Y)]

in (l a m b d a N o d e, graph4, c o u n t e r 4, f r e e V a r s 2)

Listing 4: Lambda-Terme Konstruktion

Zu Aller erst wird einen Knoten für den gesamten Ausdruck erstellt, der hat dann das Label Lambda. Danach erstellen wir einen Knoten für die Lambda-Variable x über die Hilfsfunktion „makeLeafNode“.

m a k e L e a f N o d e g r a p h l a b e l c o u n t e r = l e t g r a p h 2 = a d d N o d e g r a p h c o u n t e r l a b e l []

in (co un t er, graph2, c o u n t e r + 1)

Diese erzeugt für die Lambda-Variable einen Knoten mit dem aktuellen Zählerstand und fügt ihn in Graphen mit dem Label LAMVAR und eine leere Adjazenzliste ein (Lambda-Variablen haben keine Nachfolgernoten). Dann wird die Lambda-Variable zu den gebundenen Variblen hinzugefügt „newBoundVars“. Dieser Variablennamen wird der Knoten für Lambda-Variable der gerade erstellt wurde, zugeordnet.

Als nächster Schritt wird die Funktion f auf dem body aufgerufen. f „matcht“ ihr erstes Argument nach seinen möglichen Konstruktoren und ruft wertet ihn mit den aktuellen Graphen, den aktuellen Zählern, den aktuell gebundenen Variablen „newBoundVars“

und den aktuell freien Variablen aus. Der Graph für den body-Ausdruck wird erzeugt.

Wir holen uns den Rückgabewert des Aufrufs von f auf dem body. Dann fügen wir den Knoten Lambda dem Graphen hinzu mit Hilfe der Funktion „addNode“.

a d d N o d e g r a p h n o d e l a b e l e d g e s = Map.i n s e r t n o d e (label, e d g e s) g r a p h

Die Funktion „addNode“ macht eigentlich nicht viel. Sie nimmt einen Graphen, einen Knoten, seine Label und die Liste seiner Nachfolgerknoten und fügt sie zum Graphen als (key,value)-Paar hinzu. Um den Graph für Lambda-Ausdrücke zu erweitern, überreichen wir der Funktion „addNode“ den aktualisierten Graphen, dem Lambda-Knoten, sein Label (LAMBDA ) und die Liste seiner Nachfolgerknoten, nämlich

[(lamV arN ode, LV AR),(bodyN ode, BODY)].

Diesen Schritt haben wir genauer erläutert, weil es den Grundstein für die nachkommenden Schritte darstellt, um einen Graphen richtig zu konstruieren, nämlich garbage collection und der Isomorphietest, um auf Alpha-Äquivalenz zu schließen (sieh Definition 4.1.).

Der konstruierte Graph für einen Lch-Ausdruck entspricht einen Syntaxbaum, wo die selben Variablen durch einen einzigen Knoten repräsentiert werden. Diese zu bestimmen, müssen wir die freien und die gebundenen Variablen in einem „FreeVarsMap“ speichern.

Immer wenn eine freie variable gesehen wird, wird abgefragt, ob sie schon als frei gespei-chert wurde. Wenn ja, dann wird der Knoten für die Variable zurück gegeben, wenn nicht, wird für die Variable ein neuer Knoten erstellt und in die „FreeVarsMap“ hinzugefügt.

Die aktualisierte „FreeVarsMap“ wird für die Funktion f bei jedem rekursiven Aufruf zu Verfügung gestellt. Man kann es so verstehen, dass die „FreeVarsMap“ nach oben im

Syntaxbaum weitergereicht wird. Im Gegenteil dazu haben die gebunden Variablen einen syntaktischen Geltungsbereich. Nur die gebundenen Variablen im selben Geltungsbereich dürfen durch den selben Knoten repräsentiert werden. Die gebundenen Variablen werden auch in eine „BoundVars“ gespeichert, mit dem Unterschied, dass sie nur nach unten weitergereicht werden. D.h. bei der Auswertung eines (Unter-)Ausdrucks gelten nur die gebundenen Variablen, die in einem direkten Weg (Pfad) von der Wurzel aus zu diesem (Unter-) Ausdruck eingesammelt werden. Diese werden durch die gebundenen Variablen im Body gefunden und erweitert. Erweitert heißt, die alten Variablen mit dem selben Namen der neuen Variablen, werden durch die neuen Namen ersetzt. Die Funktion f bekommt bei jedem tieferen rekursiven Aufruf die bislang in diesen Pfad gültigen gebun-denen Variablen. Damit kann man sich beim Erstellen von Knoten und Kanten immer auf die richtige Variable beziehen.

Es ist nachvollziehbar, dass durch diese Schritte und die Konstruktionsregeln (siehe Definition 3.2.) die Umwandlung eines CH-Ausdrucks in einen Graphen stets verlustfrei möglich ist, wie das vorgeführte Beispiel anhand des Lambda-Konstruktors zeigt. Verlust-frei bedeutet, dass die Beziehungen zwischen den Teilausdrücken im gesamten Ausdruck erhalten bleiben.

Eine weitere interessante Stelle präsentiert die zweite Regel bei der Garbage collection.

Sie besteht darin, die leeren Letrec-Knoten durch deren nicht leeren Body zu ersetzen, falls es welche gibt. Da diese auch verschachtelt vorkommen können, machen diese Ersetzungen ein bisschen schwieriger. Wir wissen, dass zwischen einem leeren letrec-Knoten und seinem Body nur eine Kante existiert . sogar hat der Letrec-letrec-Knoten nur diese Kante als ausgehende Kante und sein Body nur die selbe Kante als eingehende Kante. Man kann die beiden Knoten auf eine einfache Weise verschmelzen, indem die value von leeren letrec-Knoten durch die Daten von seinem Bodyknoten im Graphen ersetzt und danach den Datensatz von Bodyknoten löscht. Weiterhin wissen wir durch die Art, wie wir den Graphen konstruieren, dass für einen letrec-Ausdruck immer der letrec-Knoten zuerst erstellt wird. Dann hat der Bodyknoten notwendigerweise eine größeren Knotennummer. Demzufolge falten wir über den Graphen von rechts mit Hilfe der Funktion Map.foldrWithKey.

Die Funktion foldrWithKey implementiert einen Foldld über den Graphen über alle Knoten, absteigend vom größten zum kleinsten Index. Dabei werden der Funktion g alle Knoten des Graphen nacheinander mit dem Knotenindex und ihm zugeordneten Daten übergeben. Ist der betrachtete Knoten ein letrec-Knoten, dann wird geprüft, ob er leer ist. Das Kriterium dafür ist, dass ein leerer letrec nur einen Nachfolgerknoten hat.

Wenn der Knoten leer ist, werden seine Daten (value) durch die Daten von seinem Body ersetzt und folglich wird der Bodyknoten gelöscht.

Wenn der betrachtete Knoten kein leerer letrec ist, wird der Graph unverändert zurück-gegeben. Ein Vorteil dieser Vorgehensweise ist, falls der Startknoten ein leerer letrec ist, dass der Startknoten nach dem Verschmelzungsschritt seinen Index behält, nämlich die Nummer null und kann direkt für das Isomorphietest benutzt werden. Nach diesem Schritt sind die Graphen in gc-Normalform. Danach werden die var-Kanten umgekehrt. Und die daraus entstandenen Graphen können unmittelbar für den Isomorphietest verwendet werden.

Für den Isomorphietest wird eine parallele Tiefensuche über den beiden Graphen ge-macht. Die Knoten-Labels und Kanten-Labels werden verglichen und bei der ersten Nicht-Übereinstimmung wird der Test abgebrochen.

Die Funktion checkIso nimmt zwei Knoten. Diese stehen immer für die Wurzeln der betrachteten Teilgraphen. Zwei Graphen, die auf Isomorphie getestet werden sollen und eine Map, wo die aktuellen Zwischenergebnisse gespeichert sind. Zuerst prüft sie für den Wurzelknoten des ersten Teilgraphen, ob es schon einem anderen Knoten in einem früheren Schritt zugeordnet worden ist. Wenn ja, dann prüft sie, ob dieser Knoten dem betrachteten Wurzelknoten aus den zweiten Teilgraph entspricht. Wenn nein, dann bricht sie ab. Falls der Knoten keinem anderen Knoten als isomorph zugeordnet wurde, dann ordnet n2 zu n1 als isomorph zu und holt sich deren Daten aus dem jeweiligen Graphen.

Dann werden ihre Label und die Anzahl der ausgehenden Kanten verglichen. Danach ruft sie sich rekursiv auf die Endknoten der beiden ersten ausgehenden Kanten. Sind alle Knoten der ersten Graphen besucht und erfolgreich als Knoten des zweiten Graphen zugeordnet, dann sind die Graphen isomorph, ansonsten nicht.

Das Ergebnis des Isomorphietests lässt direkt darauf schließen, ob die CH-Ausdrücke bis auf die unbenutzten Bindungen alpha-äquivalent sind.

t y p e I s o m o r p h i s m = Map.Map N o d e N o d e

e l s e c h e c k E d g e s (z i p e d g e s 1 e d g e s 2) iso g1 g2

- - c h e c k E d g e s

c h e c k E d g e s [] iso g1 g2 = J u s t iso

c h e c k E d g e s (((n1, l a b 1) , (n2, l a b 2)):xs) iso g1 g2

= if l a b 1 /= l a b 2 t h e n N o t h i n g e l s e c a s e c h e c k I s o n1 n2 g1 g2 iso

of (J u s t i s o 2) - > c h e c k E d g e s xs i s o 2 g1 g2 N o t h i n g - > N o t h i n g

Listing 6: Check Isomorphismus

7 Laufzeitmessung (Benchmarking)

Es wurden qualitative Tests durchgeführt, um die Korrektheit des Algorithmus zu testen. Dabei wurden repräsentative Ausdrücke Benutzt. Zur Illustration der theoretisch ermittelten Laufzeit und der Tauglichkeit des Algorithmus zu CH-Alpha-Äquivalenz-Test wurde eine Funktion zur Generierung verschachtelter CH-Ausdrucke mit unterschiedlicher Größen (Anzahl an benutzter Untertermen) und eine Funktion zur Laufzeitmessung implementiert. Als Hardware wurde ein PC mit Intel Core i7-2700k 3.50GHz, 16 GB Hauptspeicher und 64 Bit Windows 8 verwendet.

Die Messergebnisse sind in die folgende Tabelle eingetragen:

Größe des Ausdrucks CPU.time in s

100 0.00

1000 0.09

50000 0.53

10000 1.17

10000 6.73

100000 14.09

200000 31.44

300000 47.70

400000 65.45

500000 81.72

Abbildung 16: Messwerte-Tabelle

Abbildung 17: Laufzeitkurve

Wie man aus der Graphik ablesen kann, entspricht der Verlauf der Punkte einer log-linearen Kurve. Wie erwartet, stimmt die theoretisch hergeleitete Laufzeit des Algorithmus mit der gemessenen Laufzeit überein.

8 Zusamenfassung und Ausblick

Diese Arbeit hat sich mit dem Problem der Alpha-Äquivalenz für Sprachen mit rekursiven Bindungen befasst. Hierbei wurde das Problem zuerst von der komplexitätstheoretischen Perspektive betrachtet. Die Ergebnisse dieser Betrachtung haben gezeigt, dass das Pro-blem GI-vollständig und damit nicht effizient lösbar ist, da es für ProPro-bleme in dieser Komplexitätsklasse noch keine effiziente Lösung bekannt ist. Allerdings gab die Beobach-tung, dass die Schwierigkeit des Problems in direktem Zusammenhang mit der Menge der rekursiven Bindungen für letrec-Ausdrücke steht, zum Anlass das Problem genauer zu betrachten. Als erster Schritt wurde das Alpha-Äquivalenz-Problem für CH-Sprachen auf das Graph-Isomorphie-Problem für beschriftete gerichtete Graphen reduziert (LDGs), was das Problem nicht einfacher machte, denn diese Graphen sind ebenso GI-hart. Die Tatsache, dass nur letrec-Ausdrücke unbenutzte Bindungen enthalten können, lässt ahnen, dass Alpha-Äquivalenz für CH-Ausdrücke ohne unbenutzte Bindungen effizienter gelöst werden kann. Tatsächlich zeigte die Modellierung der CH-Ausdrücke als Graphen eine Ähnlichkeit zu Graphen einer Klasse, die effizient auf Isomorphie getestet werden können, nämlich die ROOLDGs. Denn diese Graphen haben für die CH-Ausdrücke eine Wurzel und deren ausgehenden Kanten sind geordnet bis auf die letrec-Ausdrücke. Daraufhin wurde

ein effizientes Verfahren präsentiert ([1] Theorem 3.13.) zur Lösung des Alpha-Äquivalenz-Problems für Sprachen mit rekursiven Bindungen ohne unbenutzten Bindungen. Dabei wurde der CH-Ausdruck als Graph modelliert unter Benutzung bestimmter Regeln (3.2.).

Als nächster Schritt wurden die unbenutzten Bindungen entfernt. Die Graphen die daraus entstanden sind, entsprachen der gc-Normalform. Diese Graphen könnte man effizient auf Isomorphie testen, indem man deren Teilgraphen zuerst testet, weil die Teilgraphen ohne var-Kanten ROOLDGs sind. Erst wenn diese Teilgraphen isomorph sind, vergleicht man deren Obergraphen, also die Graphen die der gc-Normalform entsprechen, um auf CH-Alpha-Äquivalenz der betrachteten Ausdrücke zu schließen.

Unsere Beitrag bestand darin, zu zeigen, dass man die Isomorphie und darau hin die Alpha-Äquivalenz für Ausdrücke der Sprache CH ohne die unbenutzten Bindungen (gc-Normalform) unmittelbar auf eine Art modifizierten Graphen testen kann. Diese Graphen entstehen durch Umkehren der var-Kanten. Auf diese Graphen konnte man das Isomorphie-Test und demzufolge die CH-Alpha-Äquivalenz effizient testen. Proposition.

Daraus ist das effiziente Entscheidungsprozedur-Verfahren 4.4 entstanden. Die einzelnen Schritte sind in der folgenden Graphik dargestellt.

LchT2 LchT1

T1Grraph

gcf reeGraph

M od−(gc)−Graph

T2Grraph

gcf reeGraph

M od−(gc)−Graph

Isomorphie check

Kein Äquivalenz T1 'α,gc,CH T2

Ja Nein

Abbildung 18: Alpha-Äquivalenz-Check

Dieses effiziente Entscheidungsprozedur-Verfahren wurde in die funktionale Program-miersprache Haskell implementiert. Auszüge aus dem Quellcode sind in Kapitel 6.2. Der Algorithmus wurde auf Tauglichkeit getestet, um die theoretisch hergeleitete Laufzeit zu bestätigen. Dabei wurden Ausdrücke unterschidlicher Größen verwendet, um Alltags-ausdrücke zu simulieren. Die Messdaten und die entsprechende Laufzeitkurve (siehe 7. ) zeigten eine Übereinstimmung mit der theoretisch hergeleiteten Laufzeit, nämlichnlog n mitn=|G(s)|+|G(t)|, wobei s für CH-Ausdrücke ohne unbenutzten Bindungen sind.

Listings

1 LcH-ADT . . . 47

2 Menge der Bindungen . . . 47

3 Labels . . . 48

4 Lambda-Terme Konstruktion . . . 49

5 Leere letrecs Verschmelzen . . . 51

6 Check Isomorphismus . . . 52

Abbildungsverzeichnis

4 Beispieltransformation . . . 16

7 lambda . . . 29

8 constr . . . 29

9 letrec . . . 29

10 CH-Ausdrucks-Beispielgraph . . . 30

11 gc - Konfluenzeigenschaft . . . 32

12 Beispiel-gc-free-Graph . . . 33

13 modGraph . . . 33

14 Beispiel für Syntaxbaum . . . 39

15 Geschachtelter letrec . . . 43

16 Messwerte-Tabelle . . . 53

17 Laufzeitkurve . . . 54

18 Alpha-Äquivalenz-Check . . . 56

Literatur

[1] Manfred Schmidt-Schauß and Conrad Rau and David Sabel, Al-gorithms for Extended Alpha-Equivalence and Complexity, 24th International Conference on Rewriting Techniques and Applicati-ons (RTA 2013), pages 255–270.

[2] Ingo Wegener. Complexity Theory. Exploring the Limits of Efficient Algorithms. SpringerVerlag, Heidelberg, 2005

[3] Uwe Schöning Ideen der Informatik. Grundlegende Modelle und Konzepte. Zweite Auflage. Oldenbourg Wissenschaftsverlag, Mun-chen, 2006

[4] Manfred S.schauß prg2. Skript zur Vorlesung am Institut für In-formatik, Goethe-Universität Frankfurt am Main, 2015.

[5] Manfred S.schauß EFP Skript zur Vorlesung EFP ws13/14 am Institut für Informatik, Goethe-Universität Frankfurt am Main, 2013.

[6] Church, A. (1941). The Calculi of Lambda-Conversion. Princeton University Press, Princeton, New Jersey.

[7] Georg Schnitger. Algorithmentheorie. Skript zur Vorlesung am Institut für Informatik, Goethe-Universität Frankfurt am Main, 2009.

[8] Algorithm Design von Jon Kleinberg, Pearson New International Edition (30. Juli 2013). Eva Tardos.ISBN-10: 1292023945 ISBN-13:

978-1292023946

[9] G. Snelting. Programmierparadigmen Kapitel 11 – Der untypi-sierte Lambda-Kalkül. Vorlesungsfolien, Karlsruher Institut für Technologie, 2010.

[10] BARENDREGT, HENDRIK PIETER: The Lambda Calculus, Its Syntax and Semantics, Elsevier Science Publishers, 1984

[11] J. ROGER HINDLEY, JONATHAN P. SELDIN, Lambda-Calculus and Combinators, an Introduction ISBN-13 978-0-511-41423-7 [12] Wikipedia – Die freie Enzyklopädie. www.wikipedia.org.

[13] C. Calvès and M. Fernández. Matching and alpha-equivalence check for nominal terms. J. Comput. System Sci., 76(5):283–301, 2010.

[14] Haskell-Intensivkurs: Ein kompakter Einstieg in die funktionale Programmierung, Block, M. and Neumann, A.,isbn=9783642047183,2011,Springer Berlin Heidelberg.