Björn Weinbrenner - Institut für Wirtschaftsinformatik

Werbung
Westfälische Wilhelms-Universität Münster
Ausarbeitung
Curry
im Rahmen des Seminars "Programmiersprachen"
Björn Weinbrenner
Themensteller / Betreuer: Prof. Dr. Herbert Kuchen
Institut für Wirtschaftsinformatik
Praktische Informatik in der Wirtschaft
Inhaltsverzeichnis
1
2
3
4
Allgemeines zu Curry ................................................................................................ 3
1.1
Funktional-logische Programmierung .............................................................. 3
1.2
Entstehungsgeschichte ...................................................................................... 3
1.3
Entwicklungsumgebung.................................................................................... 4
1.4
Syntax ............................................................................................................... 4
1.5
Curry-Programme ............................................................................................. 5
Typsystem.................................................................................................................. 6
2.1
Typsystem......................................................................................................... 6
2.2
Typdeklaration .................................................................................................. 6
2.3
Standardtypen ................................................................................................... 7
2.4
Listen ................................................................................................................ 7
2.5
Funktionen ........................................................................................................ 7
2.6
Constraints (Randbedingungen) ....................................................................... 8
Funktionen ................................................................................................................. 8
3.1
Allgemein.......................................................................................................... 8
3.2
Vordefinierte Operatoren und Funktionen........................................................ 9
3.3
Konditionale Ausdrücke ................................................................................... 9
3.4
Funktionen höherer Ordnung.......................................................................... 10
3.5
Lokale Variable und lokale Funktionen.......................................................... 10
3.6
Constraints ...................................................................................................... 11
3.7
Verzögerte Auswertung (lazy evaluation) ...................................................... 11
Suche........................................................................................................................ 12
4.1
Logische Variable ........................................................................................... 12
4.2
Gekapselte Suche............................................................................................ 12
4.3
Suchprinzipien ................................................................................................ 13
5
Input/Output............................................................................................................. 14
6
Vergleich Curry und Java am Beispiel Quicksort ................................................... 14
Literaturverzeichnis ........................................................................................................ 15
II
1 Allgemeines zu Curry
1.1 Funktional-logische Programmierung
Curry ist eine funktional-logische Programmiersprache. Funktionale und logische
Programmierung sind die wichtigsten Paradigmen der deklarativen Programmierung.
Deklarative Programme beschreiben das Problem selbst, nicht den Lösungsweg. Die
Lösungsfindung ist Aufgabe des Computers.
Funktionale Programmiersprachen basieren auf dem Funktionsbegriff der Mathematik.
Ein Ausdruck besteht aus verschachtelten Funktionsaufrufen. Schleifen gibt es nicht,
können aber problemlos über rekursive Funktionen dargestellt werden. Funktionen
können Funktionen höherer Ordnung sein. Ausdrücke einer funktionalen Sprache
werden deterministisch reduziert, so dass am Ende der Berechnung ein Ausdruck steht,
der nur Konstruktoren enthält.
Logische Programmierung basiert auf der Prädikatenlogik. Ausdrücke sind Suchziele,
die Unbekannte enthalten. Das Ergebnis der Berechnung sind Bindungen für diese
Unbekannten. Die Berechnung selbst ist die Suche nach diesen Bindungen. Dabei
führen Ausdrücke ggf. zu nichtdetermistischen Berechnungen.
Funktional-logische
Sprachen
vereinen
die
Eigenschaften
der
beiden
Programmierparadigmen. Ausdrücke, die Unbekannte enthalten sind sog. Constraints
(Randbedingungen). Es werden für die Auswertung dieser Constraints zwei wichtige
operationale Prinzipien unterstützt: Residuation und Narrowing. Je nach Art der
Funktionen, die innerhalb des Constraints aufgerufen werden, kommt eines der
Prinzipien zur Anwendung.
1.2 Entstehungsgeschichte
Die Sprache ist nach dem Logiker Haskell B. Curry benannt, nach dem auch die
funktionale Programmiersprache Haskell benannt wurde. Curry ist praktisch eine
Erweiterung der Sprache Haskell.
Die Sprache wird von einer internationalen Initiative zwischen Hochschulen entwickelt.
Im Bereich der Hochschullehre wird sie erfolgreich benutzt, logische und funktionale
Programmierung mit einer Sprache zu lehren.
Auf der Webseite www.informatik.uni-kiel.de/~curry/ sind praktisch alle Informationen
zum Thema Curry zusammengetragen. Dort sind auch zwei Dokumente zu finden, die
als Grundlage für Programmierer und Softwareentwickler dienen (s. Literaturhinweise).
1.3 Entwicklungsumgebung
Im "Report on Curry" [Hanus], der z.Z. den Standard der Sprache definiert, sind
Vorgaben für eine Curry-Entwicklungsumgebung zu finden. Alle Implementierungen
sollen eine interaktive Programmumgebung in Form eines Kommandozeileninterpreters
bereitstellen. Über diesen können Programmdateien geladen werden. Programme
werden nicht ausgeführt, wie es in anderen Sprachen üblich ist. Stattdessen gibt der
Benutzer einen Ausdruck über die Kommandozeile ein, der mit Hilfe der Definitionen
in den geladenen Dateien ausgewertet wird.
Es gibt einige wenige Implementierungen der Sprache Curry. Dabei handelt es sich um
meistens um Cross-Compiler, die in PROLOG, C oder Java compilieren. Deshalb ist
u.U. ein Compiler einer weiteren Sprache Vorraussetzung für die Ausführung von
Curry-Programmen.
Die bekannteste und am weitesten entwickelte Implementierung ist das PAKCS
(Portland Aachen Kiel Curry System), dass ebenso wie die Sprache Curry von mehreren
Hochschulen entwickelt wird.
Es gibt keine Implementierungen für Windows-Systeme.
1.4 Syntax
Die Syntax der Sprache entspricht weitesgehend der Syntax von Haskell. Das beinhaltet
u.a., dass die Layout-Regel zu beachten ist. Es ist syntaktisch von Bedeutung, wie weit
eine Zeile eingerückt ist.
Es wird außerdem unterstützt, dass Programme literativ geschrieben sein können. In
literativen Programmdateien werden nicht die Kommentare kenntlich gemacht, sondern
der Code. Dieser wird durch ein Symbol ("> ") eingeleitet. Alle anderen Zeilnen
werden vom Compiler ignoriert. Es ist dadurch z.B möglich, dass eine Datei
gleichzeitig HTML-Datei und Curry-Programmdatei ist.
1.5 Curry-Programme
Ein Curry-Programm ist eine Menge von Typdeklarationen und Funktionsdefinitionen.
Diese werden in die Programmumgebung geladen und können in Ausdrücken, die der
Benutzer eingibt, verwendet werden. Ein Beispiel soll den Einstieg etwas erleichtern.
Die folgenden Definitionen stehen im Programmtext.
data Bool = True | False
and :: Bool -> Bool -> Bool
and True y = y
and False _ = False
or :: Bool -> Bool -> Bool
or True _ = True
or False y = y
not :: Bool -> Bool
not True = False
not False = True
Der Benutzer stellt nun Fragen in Form von Ausdrücken an das System.
and (or True False) (and False True)
wird zu False ausgewertet. Das ist ein Beispiel für einen Ausdruck, der nur den
funktionalen Teil der Sprache ausnutzt. Der folgende Aufruf nutzt hingegen auch den
logischen Teil.
and (or True False) (and X True) =:= True
Die Ausgabe im PAKCS lautet darauf:
Free variables in goal: X
Result: success
Bindings:
X=True ?
Der eingegebene Ausdruck ist ein Contraint. Constraints werden zu success
ausgewertet, wenn Bindungen für die freien Variable gefunden werden, so dass die
Constraint-Gleichung erfüllt ist.
2 Typsystem
2.1 Typsystem
Curry
verwendet
ein
Hindley-Milner-ähnliches
polymorphes
Typsystem.
Typnotationen, wie im obigen Beispiel (z.B. not :: Int -> Int) sind nicht
erforderlich, solange der Compiler alle fehlenden Typen aus dem Kontext des
Programms rekonstruieren kann. Viele Fehler können daher beim Kompilieren
festgestellt werden.
Jedes Objekt hat einen eindeutigen Typ. Dieser Typ kann aber auch ein allgemeiner Typ
sein, der durch eine Typvariable ausgedrückt wird. Die Identitätsfunktion kann z.B.
folgendermaßen definiert werden:
id :: a -> a
id x = x
Die Operation kann auf alle Typen angewandt werden. Der Typ des Ausdrucks
entspricht dem dem Typ des übergebenen Parameters. Dieses Konstrukt wird
parametrischer Polymorphismus genannt.
Curry unterstützt keinen Ad-hoc-Polymorphismus, der das Überladen von Funktionen
erlauben würde. Die Funktion (+) kann z.B. nur auf Integerwerte angewandt werden.
Für Float-Werte muss eine andere Funktion definiert sein.
2.2 Typdeklaration
Curry bietet viele Möglichkeiten eigene Typen zu deklarieren. Im obigen Beispiel
wurde der Bool Datentyp deklariert:
data Bool = True | False
Bool ist dabei der Typkonstruktor, True und False sind Datenkonstruktoren.
Es ist erlaubt Typen rekursiv zu definieren. Eine Liste vom Typ a ist dann
folgendermaßen definiert:
data list a = nil | cons a (list a)
Eine Liste ist demnach eine leere Liste oder ein Kopfelement gefolgt von einer Liste.
cons fügt ein einzelnes Element und eine Liste zusammen.
Es lassen sich in Curry mit wenig Code komplexe Datenstrukturen (z.B. Bäume)
definieren.
2.3 Standardtypen
Curry stellt die Standardtypen bereit, die aus anderen Programmiersprachen bekannt
sind: Bool, Int, Float, Char, String.
2.4 Listen
Listen sind geordnete Zusammenstellungen von Daten. Sie sind ähnlich zu Arrays. Die
Elemente einer Liste haben alle den selben Typ.
Listen sind rekursiv definiert, ähnlich wie im obigen Beispiel. Jedoch ist die folgende
Kurzschreibweise für eine Liste möglich:
data [a] = [] | a:[a]
Der Doppelpunktoperator fügt ein Kopfelement an eine Liste an. Die leere Liste wird
als [], eine Liste vom Typ a als [a] notiert.
Listen können auch in Aufzählungsform (z.B. [1,2,3,4,5]) oder als arithmetische
Sequenz (z.B. [1..15]) geschrieben werden.
Der Datentyp String ist eine Liste von Zeichen. Der folgende Ausdruck definiert ein
Synonym String für den Typ [Char]:
type String = [Char]
2.5 Funktionen
Funktionen haben einen Typ, der aus den Typen der Parameter und dem Typ des
Funktionswertes komponiert wird. Hat eine Funktion keinen Parameter, entspricht ihr
Typ ihrem Funktionswert.
Eine Funktion add, die zwei Integerwerte addiert, hat z.B. den Typ
add :: Int -> Int -> Int
Diese Notation ist eine andere Abkürzung für
add :: Int -> (Int -> Int)
Daran läst sich ablesen, wass passiert, wenn eine Funktion mit weniger Parametern
aufgerufen wird als Ihr Typ erwartet. Die Funktion wird dann wiederum zu einer
Funktion ausgewertet. Dieses Konstrukt wird partielle Anwendung genannt. Im
folgenden Beispiel wird eine Funktion definiert, die den Nachfolger (Successor) einer
Int-Zahl berechnet und sich dabei auf der add-Funktion abstützt.
succ :: Int -> Int
succ = add 1
add wird nur mit einem Parameter aufgerufen. Der Ausdruck add 1 ist daher eine
Funktion vom Typ Int -> Int
2.6 Constraints (Randbedingungen)
Cointraint sind vom einelementigen Typ Success. Den einzigen Wert, den dieser Typ
bereitstellt, ist der Konstruktor success. Dieser wird zurückgegeben, wenn eine
Constraint-Gleichung durch Bindung der auftretenden freien Variablen erfüllt ist.
3 Funktionen
3.1 Allgemein
Funktionen sind Ausdrücke mit einem Namen, die Parameter haben können. Sie
ermöglichen einen Ausdruck mehrfach zu verwenden oder ein Problem in kleinere
Probleme zu gliedern.
Eine Funktion wird durch eine oder mehrere Funktionsgleichungen definiert. Ein
einfaches Beispiel ist die folgende Funktion:
quadrat :: Int -> Int
quadrat x = x * x
Der Funktionsaufruf geschieht dann durch Aneinanderreihung des Funktionsnamens
und seiner Parameter (z.B. quadrat 2).
Eine Funktionsgleichung kann auf der linken Seite statt Variablen auch Konstruktoren
haben. Das gilt z.B. für die Definition der Funktion not:
not True = False
not False = True
Beim Aufruf der Funktion wird für jede Funktionsgleichung geprüft, ob das "ParameterMuster" der Gleichung auf den Funktionsaufruf zutrifft (sog. Pattern-Matching). Jede
passende Gleichung wird dann angewandt.
Sind die Parameter Listen, können sie auch in Doppelpunktnotation geschrieben
werden, so dass direkt auf head und tail zugegriffen werden kann.
head :: [a] -> a
head (x:xs) = x
Werden mehr als eine Funktionsgleichung angewandt, ist die Funktion nicht
deterministisch. Sie folgt dann nicht mehr dem Funktionsbegriff der Mathematik. Wird
eine nichtdetermistische Funktion aufgerufen, die mehr als einen Rückgabewert hat,
wird nacheinander jede Lösung ausgegeben.
3.2 Vordefinierte Operatoren und Funktionen
Die Standardbibliothek von Curry enthält ähnlich zu anderen Sprachen einen
Zuweisungsoperator, arithmetische Operatoren, Vergleichsoperatoren und Boolsche
Operatoren. Eine Besonderheit ist der Constraint-Gleichheitsoperator (=:=). Alle
Operatoren sind auch Funktionen, d.h. alle erwähnten Eigenschaften von Funktionen
treffen auch auf die Operatoren zu. Sind die Operatoren geklammert, gilt die gewohnte
Präfixschreibweise von Funktionen, z.B. (+) 2 3 oder (==) x True
Es werden einige praktische Operationen auf Listen bereitgestellt. Bespiele sind Zugriff
auf das Kopfelement, Länge der Liste, die Umkehrung der Liste, das Zusammenfügen
zweier Listen.
3.3 Konditionale Ausdrücke
if_then_else bedingung ausdruck1 ausdruck2 wertet ausdruck1 aus, wenn
bedingung zutrifft, andernfalls wird ausdruck2 ausgewertet.
Um die Syntax etwas leslicher zu machen, ist auch die konventionelle Schreibweise
if bedingung then ausdruck1 else ausdruck2
zulässig. Ähnlich, aber eleganter ist die Verwendung von sogenannten Guards. Beispiel:
max x y | x > y
= x
|otherwise = y
Es werden sukzessive alle Bedingungen überprüft bis die erste Bedingung zutrifft.
otherwise wird zu True ausgewertet und leitet den "default case" ein.
3.4 Funktionen höherer Ordnung
Funktionen höherer Ordnung sind Funktionen die als Parameter oder Funktionswert
wiederum Funktionen haben.
Ein Beispiel ist die vordefinierte Funktion map. map erwartet als Parameter eine
Funktion und eine Liste und wendet dann die Funktion auf jedes Listenelement an:
map :: (a -> b) -> [a] -> [b]
map f (x:xs) = (f x) : (map f xs)
Die Funktion f wird als Parameter übergeben und kann innerhalb von map verwendet
werden.
map succ [0,1,2] wird z.B. zu [1,2,3] ausgewertet.
3.5 Lokale Variable und lokale Funktionen
Jeder Bezeichner für eine Funktion oder eine Variable hat implizit einen
Sichtbarkeitsbereich, in dem über den Bezeichner auf die Funktion oder Variable
zugegriffen werden kann. Funktionsdefinitionen auf oberster Ebene sind z.B. für alle
anderen Funktionen sichtbar. Um diese Sichtbarkeit einzugrenzen, stellt Curry zwei
Konstrukte bereit, um lokale Variable oder Funktionen zu definieren.
let ... in ... deklariert im let-Teil lokale Variable und Funktionen, die im in-
Teil verwendet werden können. Beispiel:
let x = 3 in x * x
let kann in allen Ausdrücken verwendet werden.
Eine
where-Klausel
kann
in
Funktionsdefinitionen
erscheinen.
Hinter
dem
Schlüsselwort where folgen die lokalen Definitionen. Beispiel:
f = x * x where x = 3
Hat eine lokale Funktion keinen Parameter (wie in diesen Beispielen), handelt es eine
lokale Variable. Lokale Variable werden nur einmal berechnet und der Wert wird fest
an den Bezeichner gebunden, während lokale Funktionen bei jedem Aufruf neu
berechnet werden. Nichtdeterministische lokale Funktionen müssen daher mindestens
einen Parameter haben, da die Funktion sonst als Variable gesehen wird und es zu
einem Fehler beim Kompilieren kommt.
3.6 Constraints
Constraints können in Funktionen in ähnlicher Notation auftreten wie die bereits
beschriebenen Guards. Die folgende Funktion gibt das letzte Element einer Liste aus:
last list | l ++ [x] =:= list = x where x, l free
Zu erst wird nach Werten für die freien Variablen x und l gesucht, die die ConstraintGleichung erfüllen. x und l werden an Werte gebunden und können auf der rechten
Seite der Funktionsgleichung verwendet werden.
3.7 Verzögerte Auswertung (lazy evaluation)
Ausdrücke werden in Curry erst dann berechnet, wenn ihr Wert benötigt wird. Diese
Auswertungsstrategie wird verzögerte oder faule Auswertung genannt. Das Gegenteil
ist in vielen Sprachen der Fall, in denen die Argumente einer Funktion berechnet
werden, bevor die Funktion ausgeführt wird.
In Curry kann z.B. eine unendlich lange Liste an eine Funktion übergeben werden, ohne
dass versucht wird, diese Liste vollständig zu berechnen. Es werden nur die Elemente
der Liste extrahiert, die benötigt werden.
liste = [1..]
(eine unendlich lange Liste aller Ganzzahlen beginnend mit 1)
head(x:xs) = x
Der Aufruf head liste kann in Curry berechnet werden.
4 Suche
4.1 Logische Variable
Funktionen können unbekannte Variable beinhalten, wenn diese als free deklariert sind
und in einem Constraint enthalten sind. Ein Beispiel ist die Funktion last
last list | l ++ [x] =:= list = x
where x, l free
Bevor ein Aufruf der Funktion berechnet werden kann, wird versucht, die Unbekannten
an Werte zu binden, die die Constraint-Gleichheit (=:=) erfüllen. Gelingt dies, kann der
Aufruf weiter berechnet werden. Andernfalls wird die Berechnung abgebrochen mit der
Meldung, dass das Suchziel suspendiert wurde.
4.2 Gekapselte Suche
Eine weitere Möglichkeit, nach Unbekannten zu suchen, ist die gekapselte Suche. Sie
wird durch einige im Standard enthaltene Funktionen ermöglicht. Da noch nicht alle
diese Funktionen in PAKCS implementiert sind, soll hier nur eine dieser Funktionen
vorgestellt werden:
findall :: (a -> Success) -> [a]
findall erwartet einen Parameter vom Typ (a -> Success), also eine Funktion
von einem Typ a zum Typ Success. Eine solche Funktion definiert das Suchziel. Die
Funktion goal definiert ein solches Suchziel:
goal :: (Bool -> Success)
goal x = (x || True) =:= True
findall gibt eine Liste zurück, die alle Werte enthält, die an a gebunden den
Constraint erfüllen. Der Aufruf
findall goal
wird zu [True,False] ausgewertet.
Prinzipiell kann durch die gekapselte Suche erreicht werden, dass der Suchvorgang in
einem gekapselten Ausdruck und nicht global stattfindet. Globale Suche ist in manchen
Fällen nicht effizient, da sie in der Regel nach dem Backtracking-Prinzip funktioniert.
Mit der gekapselten Suche kann mehr Einfluss auf die Art der Suche genommen
werden. Diese Möglichkeiten werden allerdings in den aktuellen Implementierungen
noch unzureichend angeboten.
4.3 Suchprinzipien
Curry unterstützt die zwei wichtigsten Prinzipien, um Lösungen für logische Variable
zu finden: Residuation und Narrowing
Residuation bedeutet, dass wenn freie Variable in einem Ausdruck vorkommen und
eine Bindung dieser Variablen nicht direkt aus dem Ausdruck abgeleitet werden kann,
erst alle anderen Ausdrücke, aus denen eine Bindung entstehen könnte, ausgewertet
werden. Erst wenn die Variable gebunden ist, wird mit der Auswertung fortgefahren. Ist
die freie Variable nicht gebunden und alle anderen Ausdrücke sind berechnet oder
warten auch auf eine Bindung, wird die Berechnung abgebrochen. Das Suchziel wird
"suspendiert".
Unter Verwendung des Narrowing-Prinzips wird versucht, die Bindung der freien
Variablen aus dem Ausdruck herzuleiten. Diese Vorgehensweise entspricht dem
Auflösen einer mathematischen Gleichung, auch wenn das in Curry nicht ohne weiteres
möglich ist.
Welche Strategie angewendet wird, hängt von der Art der Funktionen ab, in denen die
freien Variablen auftreten.
Arithmetische Operationen sind sog. starre Funktionen (rigid), die immer dem
Residuation-Prinzip folgen. Eine Berechnung der folgenden Art führt zu einer
Suspendierung:
nullstellen a b c | a*x*x + b*x + c =:= 0 = x
where x free
Andere Funktionen sind flexibel (flexible). Der Operator ++, der zwei Listen
zusammenfügt, gehört dazu, so dass die oben definierte Funktion last nach dem
Narrowing-Prinzip berechnet wird.
5 Input/Output
Unter Verwendung der bisher erläuterten Sprachmittel treten keine Seiteneffekte auf.
Das bedeutet, dass bei Aufruf einer Funktion Objekte außerhalb der Funktion nicht
verändert werden. Diese Tatsache ist charakteristisch für funktionale Sprachen.
Eingabe- und Ausgabefunktionen müssen daher besonders behandelt werden, weil diese
Seiteneffekte z.B. beim Schreiben einer Datei auftreten würden. Die Lösung des
Problems wird in Curry durch die Verwendung sog. Monaden geleistet. Monaden sind
nicht die I/O-Operationen selbst sondern Objekte, die I/O-Operationen (Aktionen)
bereitstellen. Zur Laufzeit werden diese Aktionen dann auf die Welt außerhalb des
Programms angewandt. Das Problem der Seiteneffekte tritt dann außerhalb des
funktionalen Gerüsts der Sprache aus, so dass die Grundsätze der funktionalen
Programmierung weiterhin gelten können.
Trotzdem gibt es einige Einschränkungen bezüglich der I/O-Operationen. Da
Nichtterminismus in Zusammenhang mit Ein- und Ausgabe zu Schwierigkeiten führen
kann, da z.B. der Ausführungszeitpunkt der Operationen nicht vorhersagbar ist, ist die
Verwendung von I/O-Operationen nicht innerhalb von Funktionen erlaubt, die nicht
selbst
monadisch
sind.
Innerhalb
von
monadischen
Funktionen
müssen
nichtterministische Ausdrücke gekapselt werden, z.B. durch eine gekapselte
Suchfunktion.
6 Vergleich Curry und Java am Beispiel Quicksort
Ein Vergleich zwischen Curry und einer anderen Sprache soll am Beispiel des
Sortieralgorithmus Quicksort vorgenommen werden, der in Curry und in Java
implementiert und schließlich ausgeführt wird. Der Vergleich soll bzgl. Dauer der
Implementierung, Anzahl der Zeilen des Programms und Ausführungszeit vollzogen
werden.
Die
Ausführungszeit
ist
dabei
abhängig
von
den
gewählten
Entwicklungsumgebungen, in diesem Fall Java 2 Platform Standard Edition, Version
1.4.2 und PAKCS Version 1.6.0.
Um die Ausführungszeit vergleichbar zu machen soll eine Datei mit 10.000 ZufallsIntegerzahlen gelesen werden. Diese sollen sortiert werden und in eine zweite Datei
geschrieben werden. Im Vergleich der Implementierungszeit und Code-Länge sollen
diese Lese- und Schreib-Operation allerdings unberücksichtigt bleiben und der Focus
soll nur auf dem Sortieralgorithmus liegen.
Alle drei betrachteten Größen unterscheiden sich signifikant zwischen den beiden
Sprachen.
Die Implementierungszeit betrug in Curry weniger als 5 Minuten und in Java 30
Minuten.
Das Curry-Programm lässt sich in zwei Codezeilen (im Folgenden auf 4 Zeilen verteilt)
ausdrücken, während das Java Programm 20 Zeilen einnimmt. Das Curry-Programm
sieht folgendermaßen aus:
quickSort [] = []
quickSort (x:xs) = (quickSort (filter (<= x) xs)
++ [x]
++ (quickSort (filter (> x) xs)
Die filter-Funktion ist standardmäßig definiert und filtert eine Liste unter
Verwendung eines Prädikates. Die resultierende Liste enthält also nur Werte, die dieses
Prädikat erfüllen.
In der Ausführungszeit sind ist der Unterschied allerdings ebenso prägnant. Während
das Curry-Programm 16 Sekunden für das Lesen, Sortieren und Schreiben benötigte,
brauchte das Java-Programm weniger als eine Sekunde.
Der Vergleich dieser beider Sprachen zeigt einen Grundsatz deklarativer Sprachen auf.
In deklarativen Sprachen wird das Problem und nicht dessen Lösung betrachtet. Die
Lösungsfindung wird dem Computer überlassen. Dementsprechend besteht die
Tendenz, dass die Implementierungszeit geringer ist als in anderen Sprachen. Dies
geschieht jedoch auf Kosten der Ausführungszeit.
Literaturverzeichnis
[Hanus]
Michael Hanus: Curry – An Integrated Functional Logic Language, Version
0.8.1, November 2003
[Antoy]
Sergio Antoy: Curry – A Tutorial Introduction, November 2002.
Herunterladen