Funktionales Programmieren mit objektorientierten Sprachen

Werbung
Funktionales Programmieren mit
objektorientierten Sprachen
Dr. Dieter Hofbauer
[email protected]
Hochschule Darmstadt, WS 2008/09 – p.1/21
Function Objects
In funktionalen Sprachen (Haskell, ML, . . . )
sind Funktionen “first-class citizens”. Sie können
• Argument anderer Funktionen sein,
• Resultat anderer Funktionen sein,
• in Datenstrukturen gespeichert werden,
• etc.
Wie kann man das in OO-Sprachen (Beispiel Java) imitieren?
Eine Möglichkeit: Function Objects
Hochschule Darmstadt, WS 2008/09 – p.2/21
Function Objects (Forts.)
•
Eine Funktion als die Methode eines Function Interfaces.
•
Benutze das Function Object anstelle der Funktion.
•
Beispiele:
• Comparator: Ordnen von Datenstrukturen
•
Runnable und Callable: Asynchrone Ausführung
von Aktionen im Zusammenhang mit Threads
•
ActionListener: Callback-Interfaces zur
Ausführung von Aktionen in der Zukunft
Hochschule Darmstadt, WS 2008/09 – p.3/21
Das Entwurfsmuster Command
Aus dem Buch Design Patterns von
Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides:
Encapsulate a request as an object, thereby letting you
• parameterize clients with different requests,
• queue of log requests, and
• support undoable operations.
Hochschule Darmstadt, WS 2008/09 – p.4/21
Function Objects (Forts.)
Hüllen-Interfaces für Function Objects:
• Stellt eine einstellige Funktion zur Verfügung:
public interface Function {
Object apply( Object x );
}
•
Stellt eine zweistellige Funktion zur Verfügung:
public interface Function2 {
Object apply( Object x, Object y );
}
Bemerkung: Für jede Stelligkeit ein neues Interface?
Alternative: Ausschließlich einstellige Funktionen,
aber mit Liste / Tupel von Argumenten.
Hochschule Darmstadt, WS 2008/09 – p.5/21
Function Objects: Beispiele
class ToUpperCase implements Function {
public Object apply( Object x ) {
return ( (String)x ).toUpperCase( );
}
}
Liefert das StringArgument in Großbuchstaben.
class Concat implements Function2 {
public Object apply( Object x, Object y ) {
return (String)x + (String)y;
}
}
class MyName implements Function {
public Object apply( Object x ) {
return new Concat( ).apply( "My", (String)x );
}
}
"Anna".toUpperCase( );
//
new ToUpperCase( ).apply( "Anna" );
//
new Concat( ).apply( "My", "Eclipse" ); //
new MyName( ).apply( "Eclipse" );
//
Liefert die Konkatenation der StringArgumente.
Ergänzt das StringArgument um das
Präfix "My".
"ANNA"
// üblicher Methodenaufruf
"ANNA"
// mit Function Object
"MyEclipse"
"MyEclipse"
Hochschule Darmstadt, WS 2008/09 – p.6/21
Function Objects: Beispiele (Forts.)
Ein Function Object kann auch Daten speichern.
Beispiel: Die Methode apply erwartet einen String und ergänzt ihn um ein
Präfix. Das Präfix wird bei der Konstruktion als Parameter übergeben.
class Prefix implements Function {
private String prefix;
Prefix( String prefix ) {
this.prefix = prefix;
}
public Object apply( Object x ) {
return prefix + (String)x;
}
}
new Prefix( "His" ).apply( "Eclipse" ); // "HisEclipse"
new Prefix( "Her" ).apply( "Eclipse" ); // "HerEclipse"
Hochschule Darmstadt, WS 2008/09 – p.7/21
Funktionen höherer Ordnung
Funktion Objects können Argumente
anderer Funktionen (Methoden) sein.
Beispiel:
public static List map( Function f, List list ) {
List result = new LinkedList( );
for ( Object x : list ) {
result.add( f.apply( x ) );
}
return result;
}
Die Methode map wendet die “Funktion” f auf jedes Element
der Liste list an, und gibt eine Referenz auf die so
entstehende neue Liste zurück.
System.out.println( list );
// [eins, zwei, sieben]
map( new ToUpperCase( ), list ); // [EINS, ZWEI, SIEBEN]
map( new MyName( ), list );
// [MyEINS, MyZWEI, MySIEBEN]
Hochschule Darmstadt, WS 2008/09 – p.8/21
Funktionen höherer Ordnung (Forts.)
Beispiel 2:
Die Methode filter erzeugt eine Liste derjenigen Elemente der
Liste list, die das Prädikat p erfüllen, d.h. für die p den Wert
true liefert:
public static List filter( Predicate p, List list ) {
List result = new LinkedList( );
for ( Object x : list ) {
if ( p.apply( x ) ) {
result.add( x );
}
}
return result;
}
Hochschule Darmstadt, WS 2008/09 – p.9/21
Funktionen höherer Ordnung (Forts.)
•
Stellt ein einstelliges Prädikat (Methode mit
Rückgabetyp boolean) bereit:
public interface Predicate {
boolean apply( Object x );
}
•
Testet, ob das String-Argument Länge ≤ 4 hat:
class ShortWord implements Predicate {
public boolean apply( Object x ) {
return ( (String)x ).length( ) <= 4;
}
}
System.out.println( list );
// [eins, sieben, acht]
filter( new ShortWord( ), list ); // [eins, acht]
Hochschule Darmstadt, WS 2008/09 – p.10/21
Funktionen höherer Ordnung (Forts.)
Beispiel 3: Implementiert die Komposition “f nach g”
der Funktionen f und g: (f ◦ g)(x) = f (g(x))
Die Methode apply der Komposition wendet
• zuerst das apply der “Funktion” g,
• danach das apply der “Funktion” f an.
Die Function Objects f und g werden als Konstruktorparameter übergeben.
class Composition implements Function {
private Function f, g;
Composition( Function f, Function g ) {
this.f = f;
this.g = g;
}
public Object apply( Object x ) {
return f.apply( g.apply( x ) ) ;
}
}
Hochschule Darmstadt, WS 2008/09 – p.11/21
Funktionen höherer Ordnung (Forts.)
Beispiel 3 (Forts.):
System.out.println( list ); // [eins, zwei, sieben]
Function f = new MyName( );
Function g = new ToUpperCase( );
map( new Composition( f, g ), list ); // [MyEINS, MyZWEI, MySIEBEN]
Dies ist nicht äquivalent zur Komposition in umgekehrter Reihenfolge:
map( new Composition( g, f ), list ); // [MYEINS, MYZWEI, MYSIEBEN]
Hochschule Darmstadt, WS 2008/09 – p.12/21
Funktionen höherer Ordnung (Forts.)
Beispiel 4: n-fache Iteration einer Funktion f :
Das Function Object f und die Zahl n ≥ 0 sind Konstruktorparameter.
Der Konstruktor realisiert die Gleichungen f 0 = id und f n+1 = f ◦ f n .
class Iteration implements Function {
private Function comp;
Iteration( Function f, int n ) {
if ( n < 0 )
throw new IllegalArgumentException(
"Iteration für negativen Exponent nicht definiert." );
if ( n == 0 )
comp = new Identity( );
else
comp = new Composition( f, new Iteration( f, n-1 ) );
}
public Object apply( Object x ) {
return comp.apply( x );
}
}
Hochschule Darmstadt, WS 2008/09 – p.13/21
Funktionen höherer Ordnung (Forts.)
Beispiel 4 (Forts.) Eine alternative, effizientere Implementierung:
class Iteration implements Function {
private Function f;
private int n;
Iteration( Function f, int n ) {
if ( n < 0 )
throw new IllegalArgumentException(
"Iteration für negativen Exponent nicht definiert." );
this.f = f;
this.n = n;
}
public Object apply( Object x ) {
for ( int i = n; i > 0; i-- ) {
x = f.apply( x );
}
return x;
}
}
Hochschule Darmstadt, WS 2008/09 – p.14/21
Funktionen höherer Ordnung (Forts.)
• Liefert das Argument unverändert zurück:
class Identity implements Function {
public Object apply( Object x ) {
return x;
}
}
• Liefert den um Eins inkrementierten Wert des Integer-Arguments:
class Succ implements Function {
public Object apply( Object x ) {
return (Integer)x + 1;
}
}
• Liefert den doppelten Wert des Integer-Arguments:
class Double implements Function {
public Object apply( Object x ) {
return (Integer)x * 2;
}
}
Hochschule Darmstadt, WS 2008/09 – p.15/21
Funktionen höherer Ordnung (Forts.)
Succ
succ = new Succ( );
Double doub = new Double( );
// Eins addieren:
succ.apply( 2 );
// 3
// Eins und noch eins addieren, auf zwei Arten:
succ.apply( succ.apply( 2 ) );
new Composition( succ, succ ).apply( 2 );
// 4
// 4
// Addition als iteriertes Inkrementieren:
new Iteration( succ, 100 ).apply( 2 );
// 102
// Exponentiation als iteriertes Verdoppeln:
Iterator timesEight = new Iteration( doub, 3 );
timesEight.apply( 2 );
// 16
new Iteration( timesEight, 3 ).apply( 2 ); // 1024
Hochschule Darmstadt, WS 2008/09 – p.16/21
Generic Function Objects
Statische Typsicherheit garantieren Typvariablen (ab Java 5):
public interface Function<S,T> {
S apply( T t );
}
Type-Casts werden damit überflüssig:
class ToUpperCase implements Function<String,String> {
public String apply( String x ) {
return x.toUpperCase( );
}
}
Hochschule Darmstadt, WS 2008/09 – p.17/21
Generic Function Objects (Forts.)
Analog für Funktionen höherer Ordnung:
public static <S,T>
List<S> map( Function<S,T> f, List<T> list ) {
List<S> result = new LinkedList<S>( );
for ( T x : list ) {
result.add( f.apply( x ) );
}
return result;
}
Hochschule Darmstadt, WS 2008/09 – p.18/21
Generic Function Objects: Aufgaben
• Wie sehen die anderen Klassen mit Generics aus, insbesondere
Composition und Iteration?
• Gesucht ist eine Methode, die für ein gegebenes Prädikat und eine
gegebene Liste testet, ob mindestens ein Element der Liste das
Prädikat erfüllt:
public static <T>
boolean exists( Predicate<T> p, List<T> list )
• Schreiben Sie analog eine Methode forall, die testet, ob alle
Elemente der Liste das Prädikat erfüllen.
Hochschule Darmstadt, WS 2008/09 – p.19/21
Alternative: Closures
• Closure:
Funktion, die auf freie Variablen im lexikalischen Kontext referiert.
• Funktion:
Code-Block mit Parametern; darf einen Ergebniswert haben.
• Freie Variable:
Bezeichner, den die Closure benutzt, aber nicht definiert.
Vorträge, Tutorials, Prototyp-Implementierung etc. zu Closures:
• Gilad Bracha, Neal Gafter, James Gosling, Peter von der Ahé
(aka BGGA): Closures for the Java Programming Language
http://www.javac.info/
• Vortrag (ca. 2h) von Neal Gafter (1/2007):
Advanced Topics In Programming Languages: Closures For Java
http://video.google.com/videoplay?docid=4051253555018153503
Hochschule Darmstadt, WS 2008/09 – p.20/21
Closures in Java 7: Beispiel
Eine Methode, die for-each-Schleifen via Closures realisiert:
public static <T>
void forEach( Iterable<T> seq, { T => void } f ) {
for ( T x : seq ) {
f.invoke(x);
}
}
Beispielaufruf:
List<Integer> nums = ...;
{ Integer => void } print =
{ Integer n => System.out.println( n ); };
Utils.forEach( nums, print );
Hochschule Darmstadt, WS 2008/09 – p.21/21
Herunterladen