17_Lambda_Ausdruecke.pd

Werbung
Lambda-Ausdrücke
seit Java1.8
SSJ Kapitel 21
Motivation
Man möchte ausführbaren Code über eine Variable speichern und
gegebenenfalls auch als Parameter an eine Methode oder Funktion
übergeben.
Programmcode soll wie Daten behandelt werden können!
In funktionalen Programmiersprachen (z.B. Lisp, Haskell ..) ist dies ein
Prinzip und Bestandteil der Sprache selbst.
Java 8 erlaubt eine den funktionalen Sprachen ähnliche Syntax!
Page 1
Funktionsinterface
interface Function {
int exec (int x);
}
Definition eines Interfaces mit nur
einer Funktion oder Methode nennt
man „FunctionalInterface“.
Function f ;
f = new Function() {
int exec (int x) { return x * x; }
Erzeugen einer anonymen Klasse
und Zuweisung zur Variablen f.
}
f.exec(3);
// ergibt 9
f = new Function() {
int exec (int x) { return x + x; }
}
f.exec(3);
Erzeugen einer neuen anonymen
Klasse und Zuweisung zur
Variablen f.
// ergibt 6
Einfache Lambda-Ausdrücke
Parameter -> Ausdruck
Lambda-Ausdrücke bilden Parameter auf
einen Wert ab.
z.B.
x -> x*x
// Lambda-Ausdruck
Lambda-Ausdrücke können an
Functionalinterfaces zugewiesen werden.
Die Typen werden durch die exec Methode
festgelegt (hier alle int ).
Funktionen könne wie Objekte zugewiesen
werden.
interface Function { int exec (int x); }
Function f = x -> x*x ;
f.exec(3);
Die Typen des Parameters x als auch des
Wertes des Ausdrucks müssen später aus
dem Kontext vom Compiler erschlossen
werden.
// ergibt 9
Function f = x -> x*x ;
wird vom Compiler automatisch ersetzt zu
f = new Function() {
int exec (int x) { return x*x; }
}
Page 2
Der Compiler erzeugt intern die
entsprechende anonyme Klasse.
Einfache Lambda-Ausdrücke (2)
f = x ->x+x;
// f.exec(10) ergibt 20
f = y ->13+y;
// f.exec(10) ergibt 23
Beliebige Lambda-Ausdrücke die mit
dem Functionalinterface von „Function“
kompatibel sind, können einer Variablen
vom Typ Function zugewiesen werden.
int[] map(Function f, int[] data) {
z.B. Methode map erwartet einen
Parameter vom Typ Function und kann
deshalb auf die Methode exec von
Function zugreifen.
int[] result = new int[data.lengt];
for(int i = 0; i<data.length; i++)
result[i] = f.exec(data[i]);
return result;
}
int[] data = {1,2,3}; int[] result;
result = map(x->x+1, data);
// ergibt 2,3,4
result = map(x->x*x, data);
// ergibt 1,4,9
Lambda-Ausdrücke können nun direkt
an eine Methode die ein passendes
Functionalinterface erwartet
übergeben werden (hier Function).
Lambda-Ausdrücke mit mehreren Parametern
interface Function0 { int exec( ); }
interface Function1 { int exec( int x, int y); }
interface StringFunction { String exec(String s, int x ); }
Function0 f0 = ()-> 42;
// f0.exec() ergibt 42
Function1 f1 = (a,b)-> 2*a +a*b;
// f2.exec(3,4) ergibt 18
StringFunction sf = (s,pos)-> s.substring(pos); // sf.exec(“Hallo Du”,6) ergibt “Du”
Function1 f2 = (int a, int b)-> 2*a +a*b;
// Parametertyp kann angegen werden.
Function0 f3 = (int x)-> x*x;
// Wenn ein Typ angegeben wird, dann
// ist eine Klammer notwendig.
Page 3
Lamda Syntax
lambda = ArgList "->" Body
ArgList = Identifier
| "(" Identifier [ "," Identifier ] ")"
| "(" Type Identifier [ "," Type Identifier ] ")"
| "()"
Body = Expression | "{" [ Statement ";" ]+ "}"
ArgList
->
Body
x
->
x + 1;
(x)
(x)
->
->
x + 1;
{ return x + y;
} ;
(x,y)
->
{ x = 10 * x ;
return x + y;
} ;
(int x, int y)
->
x + y;
()
->
System.out.println("Hello World");
// Expression
// Statement
7
Lambda-Ausdrücke mit Anweisungsteil
interface Action0 { void exec (); }
Action0 printHello = () -> System.out.print(“Hello”);
interface Action2 { void exec (chr ch, int n); }
Interface mit Methode ohne
Parameter und Rückgabewert void .
Interface mit Methode mit Übergabeparameter und Rückgabewert void .
Action2 printLine = (ch, n ) -> {
for(int i =0; i<n; i++) System.out.print(ch);
System.out.println();
Lambda-Ausdruck mit Anweisungsteil in { }.
}
interface MyFunction { int exec (int a, int b); }
MyFunction ggt = (x, y) -> {
int rest = x%y;
while(rest !=0) {
x=y; y= rest ; rest = x%y;
}
return y;
}
int res = ggt.exec(24, 15);
// ergibt 3
Page 4
Da die Methode exec vom Typ int ist
muss ein Statement mit return einen
Wert von Typ int zurückgeben.
Methodenreferenzen
interface Function2 { int exec (int a, int b); }
Function2 f = Math::min ;
f.exec(2,5);
// ergibt 2
f = Math::min ;
wird vom Compiler automatisch ersetzt zu
Da der Lambda-Ausdruck nur eine
namenlose Methode ist, darf man ihm in
Java eine bekannte Methode zuweisen.
Statische als
ClassName::MethodName
bei nicht statischen
objectName::MethodName
Der Compiler erzeugt intern die
entsprechende anonyme Klasse und
überprüft ob die im Interface definierten
Parameter zur Methode passen.
f = new Function2() {
int exec (int a, int b) {
return Math.min(a,b);
}
}
java.util.function
(seit Java 8)
java.util.function stellt ein Vielzahl vordefinierter " Functionalinterfaces" unter Verwendung von
Generics zur Verfügung.
/**
* Represents a function that accepts one argument and produces a result.
* This is a functional interface whose functional method is apply(Object).
* @param <T> the type of the input to the function
* @param <R> the type of the result of the function
@Name ist eine Annotation, welche auf den
Ablauf des Programms keine Einfluss hat.
*/
Bestimmte Annotationen werden vom Compiler
zur Überprüfung genutzt (hier, ob das interface
auch nur eine abstracte Methode besitzt).
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
Verwendung
Function<Integer, Integer> pow = (i) -> i * i;
Function<Integer, Integer> abs = Math::abs;
System.out.println(pow.apply(-5));
System.out.println(abs.apply(-5));
Page 5
// Ausgabe: 25
// Ausgabe: 5
java.util.function @FunctionalInterface
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
Anwendungsbeispiel
public static int mySum(List<Integer> numbers, Predicate <Integer> p) {
int out = 0;
for (int i : numbers) {
if (p.test(i)) { out += i; }
}
return out;
}
int sum = mySum (list, x -> x % 2 == 0);
Interfaces mit "default" Methoden
Bisher (vor Java8) konnten Interfaces keine implementierte Methoden enthalten.
Wollte man aber ein Interface erweitern, musste jede Klasse, die
davon erbt, die neue Methode implementieren.
Jedes Programm, dass für eine ältere Version geschrieben war, konnte
daher nicht mehr funktionieren.
Default Methoden geben die Möglichkeit, Interfaces um grundlegende
Funktionen zu erweitern. Zusätzlich ermöglichen sie es, Interfaces wie
das FunctionalInterfaces mit zusätzlichen Hilfs-Methoden
auszustatten.
@FunctionalInterface
public interface Predicate<T> {
boolean
test(T t);
}
default Predicate<T> negate() {
return (t) -> !test(t); }
}
Eine default Methode kann keine Member Variablen verändern und ist
auf die Verwendung der abstracten Interface-Methoden angewiesen.
Page 6
java.util.function Predicate (siehe API)
Das Predicate FunctionalInterface wie in der API beschrieben.
@FunctionalInterface
public interface Predicate<T>
default
static
Predicate<T>
<T> Predicate<T>
default
Predicate<T>
and(Predicate<? super T> other)
Returns a composed
predicate that represents a short-circuiting logical AND of this
predicate and another.
isEqual( Object targetRef
) Returns a predicate
that tests if two arguments are equal according to
Object.equals(Object,Object).
or(Predicate<? super T> other) Returns a predicate
that represents the logical negation of this predicate.
default
Predicate<T>
negate() Returns a composed
predicate that represents a
short-circuiting logical OR of this predicate and another.
boolean
test(T t)
Evaluates this predicate on the given argument.
Beispiel 1 zu Predicate
import java.util.function.*;
class MyPredicateTest {
public static void main ( String[] args){
Predicate<Integer> greaterThanTen = (i) -> i > 10;
Predicate<Integer> lowerThanTwenty = (i) -> i < 20;
boolean result = greaterThanTen.or(lowerThanTwenty).negate().test(15);
System.out.print(result );
}
}
Page 7
Beispiel 2 zu Predicate
import java.util.function.*;
class MyPredicateTest {
static void process(int number, Predicate<Integer> predicate) {
if (predicate.test(number)) {
System.out.println("Number " + number + " was accepted!");
}
}
public static void main ( String[] args){
process(10, (i) -> i > 7);
}
}
Zur Erinnerung: FunctionPlotter mit abstracter
Funktion aus der Vorlesung zum AWT.
jawa.awt.Frame
jawa.awt.WindowListener
INTERFACE
FunctionPlotter
WindowSize;
XYCoordinates;
FunctionPlotter;
paint;
function;
getPixel_fromXY;
SinXPlotter
XCosXPlotter
TanXPlotter
double function (double x );
double function (double x );
double function (double x );
SinXPlotter
XCosXPlotter
TanXPlotter
main;
main;
main;
Page 8
... .. .
Beispiel: Code der abstrakten Klasse FunctionPlotter
import java.awt.* ;
public abstract class FunctionPlotter
extends java.awt.Frame {
private int noSamples;
private double minx;
...........
private int winx;
private int winy;
// samples to be plotted
// minimal plotted x-Value
/** This method is invoked automatically by the
* runtime-environment whenever the contents
* of the window is displayed on the screen and
* (once everytime a redraw event occures)
* plotting of the graph of f(x) is implemented here
*/
public void paint (Graphics g) {
// horiz. window size in pixel
// vertical window size in pixel
int i;
int x1,x2,y1,y2;
double step = (maxx - minx) / (noSamples -1);
double x = minx;
g.setColor( Color.red);
// Constructor for the diplay domain
public FunctionPlotter ( int _noSamples,
int _winx, int _winy,
double _minx, double _maxx,
double _miny, double _maxy) {
.............
setSize (winx,winy);
setVisible(true);
}
x2 = getPixelXfromWorldX( x );
y2 = getPixelYfromWorldY( f( x ) );
for(i = 1; i< noSamples; i++){
x+= step;
x1 = x2; y1 = y2;
x2 = getPixelXfromWorldX ( x
);
y2 = getPixelYfromWorldY ( f(x) );
// Transforanitons from World to Pixel Koordinates
public int getPixelXfromWorldX( double x){
return (int) ( (x - minx) / (maxx - minx)* winx) ;
}
public int getPixelYfromWorldY( double y){ ...... }
/**
* Abstract method to be implemented in child classes
* to evaluate the function to be plotted
*/
public abstract double f ( double x);
g.drawLine(x1,y1,x2,y2);
}
}
}
Beispiel: Rahmen zum Zeichnen reeller Funktionen
Um eine Funktion, wie z. B. x  x*cos(x) plotten zu können, mussten wir
eine Klasse definieren,
• die FunctionPlotter erweitert,
• und in der f durch die gewünschte Funktion implementiert ist.
Außer einem entsprechenden Konstruktor muss in dieser Klasse keine
weitere Methode definiert sein.
Page 9
früher
heute
Die Funktion wird dem Konstruktor
der Plotter-Klasse übergeben
Implementierte abstrakte Klasse
/**
* Concrete Class which implements function f()
* to calculate x times cos(x)
*/
/**
* Class uses FunctionalPlotter-Class to plott various functions
*/
public class Application {
public class XCosXPlotter extends FunctionPlotter {
public XCosXPlotter( int nSamples,
int _winx, int _winy,
double _minx, double _maxx,
double _miny, double _maxy )
{
super( nSamples, _winx,_winy, _minx, _maxx, _miny,_maxy);
}
public double f(double x) { return
x* Math.cos(x);
}
public static void main( String args[]) {
new XCosXPlotter( 50, 300,200, 0.0, 10.0, -1.5, 1.5);
}
public static void main( String args[]) {
new FunctionalPlotter( x-> x*Math.cox(x), 50, 300,200,
0.0, 10.0, -1.5, 1.5);
}
new FunctionalPlotter( x-> x*Math.sin(x), 50, 300,200,
0.0, 10.0, -1.5, 1.5);
}
}
FunctionalPlotter mit Lambda-Ausdruck
initialisieren.
jawa.awt.Frame
jawa.awt.WindowListener
INTERFACE
FunctionalPlotter
WindowSize;
Die Klasse FunctionalPlotter hat nun ein Funktion f vom Typ
des FunctionalInterfaces Function<Double,Double>.
XYCoordinates;
FunctionalPlotter;
paint;
Function;
Über den Konstruktor
FunctionalPlotter( Function<Double,Double>, ……)
kann die Klasse mit beliebigen Funktionen initialisiert
werden.
getPixel_fromXY;
Application
main;
In einer beliebigen Anwendung kann nun die Klasse
FunctionalPlotter genutzt werden und mit einer
beliebigen Funktion, über einen Lambda-Ausdruck im
Konstruktor (wie z. B.
new FunctionalPlotter( x  x*cos(x), ……) )
aufgerufen werden .
Page 10
Früher mit abstrakten f
neu mit FunctionalInterface
import java.awt.* ;
import java.awt.* ;
import.util.function.Function
public abstract class FunctionPlotter
extends java.awt.Frame {
public class FunctionalPlotter
extends java.awt.Frame {
private int noSamples; //
private double minx;
...........
private int winx;
private int winy;
samples to be plotted
// minimal plotted x-Value
// horiz. window size in pixel
// vertical window size in pixel
private Function<Double,Double> f;
private int noSamples; // samples to be plotted
private double minx; // minimal plotted x-Value
...........
private int winx;
// window size in pixel
private int winy;
// vertical window size in pixel
// Constructor for the diplay domain
public FunctionPlotter (
int _noSamples,
int _winx, int _winy,
double _minx, double _maxx,
double _miny, double _maxy) {
.............
setSize (winx,winy);
setVisible(true);
}
// Transforanitons from World to Pixel Koordinates
public int getPixelXfromWorldX( double x){
return (int) ( (x - minx) / (maxx - minx)* winx) ;
}
public int getPixelYfromWorldY( double y){ ...... }
/**
// Constructor for the diplay domain
public FunctionalPlotter (Function<Double,Double> _f,
int _noSamples,
int _winx, int _winy,
double _minx, double _maxx,
double _miny, double _maxy) {
.............
f=_f;
setSize (winx,winy);
setVisible(true);
}
// Transforanitons from World to Pixel Koordinates
public int getPixelXfromWorldX( double x){
return (int) ( (x - minx) / (maxx - minx)* winx) ;
}
public int getPixelYfromWorldY( double y){ ...... }
* Abstract method to be implemented in child classes
* to evaluate the function to be plotted
*/
public abstract double f ( double x);
Früher mit abstrakten f
/** This method is invoked automatically by the
* runtime-environment whenever the contents
* of the window is displayed on the screen and
* (once everytime a redraw event occures)
* plotting of the graph of f(x) is implemented here
*/
public void paint (Graphics g) {
neu mit FunctionalInterface
/** This method is invoked automatically by the
* runtime-environment whenever the contents
* of the window is displayed on the screen and
* (once everytime a redraw event occures)
* plotting of the graph of f.apply(x) is implemented here
*/
public void paint (Graphics g) {
int i;
int x1,x2,y1,y2;
double step = (maxx - minx) / (noSamples -1);
double x = minx;
g.setColor( Color.red);
int i;
int x1,x2,y1,y2;
double step = (maxx - minx) / (noSamples -1);
double x = minx;
g.setColor( Color.red);
x2 = getPixelXfromWorldX( x );
y2 = getPixelYfromWorldY( f( x ) );
x2 = getPixelXfromWorldX( x );
y2 = getPixelYfromWorldY( f.apply( x ) );
for(i = 1; i< noSamples; i++){
x+= step;
x1 = x2; y1 = y2;
x2 = getPixelXfromWorldX ( x
);
y2 = getPixelYfromWorldY ( f(x) );
for(i = 1; i< noSamples; i++){
x+= step;
x1 = x2; y1 = y2;
x2 = getPixelXfromWorldX ( x
);
y2 = getPixelYfromWorldY ( f.apply(x) );
g.drawLine(x1,y1,x2,y2);
g.drawLine(x1,y1,x2,y2);
}
}
}
}
}
}
Page 11
Herunterladen