-
Uvod Funkcije Seznami Leni seznami Zaključek
Funkcije, neskončna zaporedja in java
LALGinar, 4. oktober 2013
Luka Fürst
-
Uvod Funkcije Seznami Leni seznami Zaključek
Funkcijsko programiranje
• Funkcije kot osnovni gradniki• funkcije kot argumenti funkcij• funkcije, ki vračajo funkcije
-
Uvod Funkcije Seznami Leni seznami Zaključek
Funkcijsko programiranje
• Funkcije kot osnovni gradniki• funkcije kot argumenti funkcij• funkcije, ki vračajo funkcije
• Sklicevalna preglednost (referential transparency)• klic funkcije z določenim naborom vrednosti parametrov vedno
vrne isti rezultat, ne glede na okolǐsčine klica• posledica: odpor do spremenljivk (oz. stanja nasploh)• predstavitev časovne variacije s seznami
-
Uvod Funkcije Seznami Leni seznami Zaključek
Funkcijsko programiranje
• Funkcije kot osnovni gradniki• funkcije kot argumenti funkcij• funkcije, ki vračajo funkcije
• Sklicevalna preglednost (referential transparency)• klic funkcije z določenim naborom vrednosti parametrov vedno
vrne isti rezultat, ne glede na okolǐsčine klica• posledica: odpor do spremenljivk (oz. stanja nasploh)• predstavitev časovne variacije s seznami
• Leno vrednotenje• objekt ovrednotimo šele tedaj, ko njegovo vrednost zares
potrebujemo
-
Uvod Funkcije Seznami Leni seznami Zaključek
Funkcijsko programiranje
• Funkcije kot osnovni gradniki• funkcije kot argumenti funkcij• funkcije, ki vračajo funkcije
• Sklicevalna preglednost (referential transparency)• klic funkcije z določenim naborom vrednosti parametrov vedno
vrne isti rezultat, ne glede na okolǐsčine klica• posledica: odpor do spremenljivk (oz. stanja nasploh)• predstavitev časovne variacije s seznami
• Leno vrednotenje• objekt ovrednotimo šele tedaj, ko njegovo vrednost zares
potrebujemo
• H. Abelson, G.J. Sussman, J. Sussman: Structure andInterpretation of Computer Programs (2. izdaja), MIT Press,1996.
-
Uvod Funkcije Seznami Leni seznami Zaključek
Funkcijsko programiranje in java
• Java ne ponuja konstruktov funkcijskega programiranja
-
Uvod Funkcije Seznami Leni seznami Zaključek
Funkcijsko programiranje in java
• Java ne ponuja konstruktov funkcijskega programiranja
• Zanimiva in poučna pa je simulacija tovrstnih konstruktov vjavi
-
Uvod Funkcije Seznami Leni seznami Zaključek
Funkcijsko programiranje in java
• Java ne ponuja konstruktov funkcijskega programiranja
• Zanimiva in poučna pa je simulacija tovrstnih konstruktov vjavi
• V javi lahko (do določene mere) programiramo po funkcijsko. . .
-
Uvod Funkcije Seznami Leni seznami Zaključek
Funkcijsko programiranje in java
• Java ne ponuja konstruktov funkcijskega programiranja
• Zanimiva in poučna pa je simulacija tovrstnih konstruktov vjavi
• V javi lahko (do določene mere) programiramo po funkcijsko. . .
• . . . toda za ceno okorne in dolgovezne sintakse
-
Uvod Funkcije Seznami Leni seznami Zaključek
Funkcije v javi
• Osnovo za predstavitev funkcij predstavljajo sledeči vmesniki:
// zero-argument function
interface F0 {
R apply();
}
// one-argument function
interface F1 {
R apply(P param);
}
// two-argument function
interface F2 {
R apply(P1 param1, P2 param2);
}
// unary predicate
interface Predicate
{
boolean satisfies(P param);
}
-
Uvod Funkcije Seznami Leni seznami Zaključek
Funkcije v javi
• Funkcijo predstavimo kot objekt tipa F0, F1, F2 ali Predicate
class Square implements F1 {
public Integer apply(Integer a) {
return (a * a);
}
}
// ...
F1 sq = new Square();
System.out.println(sq.apply(5)); // 25
-
Uvod Funkcije Seznami Leni seznami Zaključek
Funkcije v javi
• Funkcijo predstavimo kot objekt tipa F0, F1, F2 ali Predicate
class Square implements F1 {
public Integer apply(Integer a) {
return (a * a);
}
}
// ...
F1 sq = new Square();
System.out.println(sq.apply(5)); // 25
• Kot objekt brezimenskega implementatorja vmesnika:
F1 sq = new F1() {
public Integer apply(Integer a) {
return (a * a);
}
};
• Več primerov: Scale, Plus, DivisibleBy
-
Uvod Funkcije Seznami Leni seznami Zaključek
Funkcije, ki sprejemajo in/ali vračajo funkcije
• Kompozitum f ◦ g :
public static F1 composition(
final F1 f, final F1 g) {
return new F1() {
public R apply(P param) {
return f.apply(g.apply(param));
}
};
}
// ...
F1 sq = new Square();
F1 dbl = new Scale(2);
F1 sqOfDbl = composition(sq, dbl);
F1 dblOfSq = composition(dbl, sq);
System.out.println(sqOfDbl.apply(3)); // 36
System.out.println(dblOfSq.apply(3)); // 18
• Negacija predikata
-
Uvod Funkcije Seznami Leni seznami Zaključek
Memoizacija
• Shranjevanje rezultatov funkcije pri določenih vrednostih
• Pri podanem naboru parametrov se funkcija izvede le enkrat
• V naslednjih klicih se uporabi shranjeni rezultat
-
Uvod Funkcije Seznami Leni seznami Zaključek
Memoizacija
• Shranjevanje rezultatov funkcije pri določenih vrednostih
• Pri podanem naboru parametrov se funkcija izvede le enkrat
• V naslednjih klicih se uporabi shranjeni rezultat
public static F1 memoize(final F1 f) {
return new F1() {
Map memo = new HashMap();
public R apply(P param) {
if (memo.containsKey(param)) {
return memo.get(param);
}
R result = f.apply(param);
memo.put(param, result);
return result;
}
};
}
-
Uvod Funkcije Seznami Leni seznami Zaključek
Seznami v javi
• Dve vrsti seznamov:• prazen seznam• seznam z glavo in repom
• glava je lahko karkoli, rep pa mora biti seznam
abstract class List {
public abstract T head();
public abstract List tail();
public abstract boolean isEmpty();
}
class Empty extends List {
public T head() {
throw new NoSuchElementException();
}
public List tail() {
throw new NoSuchElementException();
}
public boolean isEmpty() {
return true;
}
}
-
Uvod Funkcije Seznami Leni seznami Zaključek
Seznami v javi
class Nonempty extends List {
private T head;
private List tail;
public Nonempty(T head, List tail) {
this.head = head;
this.tail = tail;
}
public T head() {
return head;
}
public List tail() {
return tail;
}
public boolean isEmpty() {
return false;
}
}
-
Uvod Funkcije Seznami Leni seznami Zaključek
Tipične metode seznamov
// returns the element at index ix
public T get(int ix) {
if (ix < 0) {
throw new IllegalArgumentException("get: index < 0");
}
if (ix == 0) {
return head();
}
return tail().get(ix-1);
}
// returns a list comprising the elements indexed ixFirst, ..., ixLast
// of this list
public List sublist(int ixFirst, int ixLast) {
if (ixFirst < 0 || ixFirst > ixLast) {
return new Empty();
}
if (ixFirst == 0) {
return new Nonempty(head(), tail().sublist(0, ixLast-1));
}
return tail().sublist(ixFirst-1, ixLast-1);
}
-
Uvod Funkcije Seznami Leni seznami Zaključek
Tipične metode seznamov
// returns a new list obtained by applying func to individual elements
// of this list
public List map(F1 func) {
if (isEmpty()) {
return new Empty();
}
return new Nonempty( func.apply(head()), tail().map(func) );
}
// returns a list of all elements that satisfy the given predicate
public List filter(Predicate pred) {
if (isEmpty()) {
return new Empty();
}
if (pred.satisfies(head())) {
return new Nonempty(head(), tail().filter(pred));
}
return tail().filter(pred);
}
-
Uvod Funkcije Seznami Leni seznami Zaključek
Tipične metode seznamov
// returns true iff any element satisfies the given predicate
public boolean any(Predicate pred) {
if (isEmpty()) {
return false;
}
if (pred.satisfies(head())) {
return true;
}
return tail().any(pred);
}
// returns true iff all elements satisfy the given predicate
public boolean all(Predicate pred) {
if (isEmpty()) {
return true;
}
if (!pred.satisfies(head())) {
return false;
}
return tail().all(pred);
}
-
Uvod Funkcije Seznami Leni seznami Zaključek
Tipične metode seznamov
// returns func(...(func(func(initialValue, [0]), [1]), [2]), ..., [n])
public T leftFold(F2 func, T initialValue) {
if (isEmpty()) {
return initialValue;
}
return tail().leftFold(func, func.apply(initialValue, head()));
}
// returns func([0], func([1], (... func([n], initialValue)...)))
public T rightFold(F2 func, T initialValue) {
if (isEmpty()) {
return initialValue;
}
if (tail().isEmpty()) {
return func.apply(head(), initialValue);
}
return func.apply(head(), tail().rightFold(func, initialValue));
}
-
Uvod Funkcije Seznami Leni seznami Zaključek
Motivacija
• Problem: poǐsči drugo praštevilo na intervalu [a, b]
-
Uvod Funkcije Seznami Leni seznami Zaključek
Motivacija
• Problem: poǐsči drugo praštevilo na intervalu [a, b]
• Proceduralna rešitev:
public static int secondPrime(int a, int b) {
int counter = 0, num = a;
while (num
-
Uvod Funkcije Seznami Leni seznami Zaključek
Motivacija
• Problem: poǐsči drugo praštevilo na intervalu [a, b]
• Proceduralna rešitev:
public static int secondPrime(int a, int b) {
int counter = 0, num = a;
while (num
-
Uvod Funkcije Seznami Leni seznami Zaključek
Motivacija
• Elegantneǰsa (“funkcijska”) rešitev:
public static int secondPrime(int a, int b) {
Predicate isPrime = ...;
List interval = List.intInterval(a, b); // [a, ..., b]
List primes = interval.filter(isPrime);
return primes.get(1);
}
-
Uvod Funkcije Seznami Leni seznami Zaključek
Motivacija
• Elegantneǰsa (“funkcijska”) rešitev:
public static int secondPrime(int a, int b) {
Predicate isPrime = ...;
List interval = List.intInterval(a, b); // [a, ..., b]
List primes = interval.filter(isPrime);
return primes.get(1);
}
• Pomanjkljivosti postanejo očitne pri npr. a = 106, b = 107
-
Uvod Funkcije Seznami Leni seznami Zaključek
Motivacija
• Elegantneǰsa (“funkcijska”) rešitev:
public static int secondPrime(int a, int b) {
Predicate isPrime = ...;
List interval = List.intInterval(a, b); // [a, ..., b]
List primes = interval.filter(isPrime);
return primes.get(1);
}
• Pomanjkljivosti postanejo očitne pri npr. a = 106, b = 107
• Rešitev: leni seznami
-
Uvod Funkcije Seznami Leni seznami Zaključek
Leni seznami
• Alternativno ime: tokovi (streams)
• Enaka struktura kot pri navadnih seznamih
• Prvi element lenega seznama se ovrednoti že ob izdelavi
• Naslednji elementi se vrednotijo po potrebi
-
Uvod Funkcije Seznami Leni seznami Zaključek
Leni seznami
• Abstraktni nadrazred in razred za prazen leni seznam stadefinirana enako kot pri navadnih seznamih:
class LazyList {
public abstract T head();
public abstract LazyList tail();
public abstract boolean isEmpty();
}
class Empty extends LazyList {
public T head() { throw new NoSuchElementException(); }
public LazyList tail() {throw new NoSuchElementException();}
public boolean isEmpty() { return true; }
}
-
Uvod Funkcije Seznami Leni seznami Zaključek
Neprazen leni seznam
• Rep je funkcija, ki se ovrednoti šele ob klicu metode tail
class Nonempty extends LazyList {
private T head;
private F0 lazyTail;
public Nonempty(T head, F0 lazyTail) {
this.head = head;
this.lazyTail = memoize(lazyTail);
}
public T head() {
return head;
}
public LazyList tail() {
return lazyTail.apply();
}
public boolean isEmpty() {
return false;
}
}
-
Uvod Funkcije Seznami Leni seznami Zaključek
Celoštevilski interval [a, b]
• Z navadnim seznamom:
public static List intInterval(int a, int b) {
if (a > b) {
return new Empty();
}
return new Nonempty(a, intInterval(a + 1, b));
}
• Z lenim seznamom:
public static LazyList intInterval(final int a, final int b) {
if (a > b) {
return new Empty();
}
return new Nonempty(a, new F0() {
public LazyList apply() {
return intInterval(a + 1, b);
}
});
}
-
Uvod Funkcije Seznami Leni seznami Zaključek
Metoda filter pri lenih seznamih
public LazyList filter(final Predicate pred) {
if (isEmpty()) {
return new Empty();
}
if (pred.satisfies(head())) {
return new Nonempty(head(), new F0() {
public LazyList apply() {
return tail().filter(pred);
}
});
}
return tail().filter(pred);
}
• Seznam se do prvega elementa, ki zadošča predikatu, preiskuje“neučakano” (eagerly)
• Po odkritju prvega takega elementa delo na seznamu počakado zahteve po naslednjem ustreznem elementu
-
Uvod Funkcije Seznami Leni seznami Zaključek
Drugo praštevilo na intervalu [a, b]
public static int secondPrime(int a, int b) {
Predicate isPrime = ...;
LazyList interval = LazyList.intInterval(a, b);
LazyList primes = interval.filter(isPrime);
return primes.get(1);
}
• Ob klicu metode filter se poǐsče samo prvo praštevilo
• Drugo praštevilo (in naslednja, če bi jih zahtevali) se poǐsčejošele ob klicu metode get
-
Uvod Funkcije Seznami Leni seznami Zaključek
Neskončni leni seznami
• Lene sezname lahko uporabimo za predstavitev neskončnihzaporedij
-
Uvod Funkcije Seznami Leni seznami Zaključek
Neskončni leni seznami
• Lene sezname lahko uporabimo za predstavitev neskončnihzaporedij
• Seznam vseh naravnih števil:
public static LazyList ints() {
return intsFrom(1);
}
public static LazyList intsFrom(final int n) {
return new Nonempty(n, new F0() {
public LazyList apply() {
return intsFrom(n+1);
}
});
}
// ...
System.out.println(ints().sublist(0, 4)); // [1, 2, 3, 4, 5]
-
Uvod Funkcije Seznami Leni seznami Zaključek
Neskončni leni seznami
public static LazyList makeList() {
return gen(0, 1);
}
private static LazyList gen(final int a, final int b) {
return new Nonempty(a, new F0() {
public LazyList apply() {
return gen(b, a+b);
}
});
}
-
Uvod Funkcije Seznami Leni seznami Zaključek
Fibonaccijevo zaporedje
public static LazyList fibs() {
return fibgen(0, 1);
}
private static LazyList fibgen(final int a, final int b) {
return new Nonempty(a, new F0() {
public LazyList apply() {
return fibgen(b, a+b);
}
});
}
// ...
System.out.println(fibs().sublist(5, 9)); // [5, 8, 13, 21, 34]
-
Uvod Funkcije Seznami Leni seznami Zaključek
Eratostenovo sito (postopek iskanja praštevil)
• Prični s seznamom [2, 3, 4, . . . ]
• Dodaj prvi element seznama v seznam praštevil
• Odstrani prvi element seznama in vse njegove večkratnike
• Rekurzivno ponovi postopek na preostalem seznamu
-
Uvod Funkcije Seznami Leni seznami Zaključek
Eratostenovo sito (postopek iskanja praštevil)
• Prični s seznamom [2, 3, 4, . . . ]
• Dodaj prvi element seznama v seznam praštevil
• Odstrani prvi element seznama in vse njegove večkratnike
• Rekurzivno ponovi postopek na preostalem seznamu
public static LazyList primes() {
return sieve(intsFrom(2));
}
private static LazyList sieve(final LazyList list) {
return new Nonempty(
list.head(),
new F0() {
public LazyList apply() {
return sieve( list.tail().filter(
negate(new DivisibleBy(list.head()))) );
}
});
}
-
Uvod Funkcije Seznami Leni seznami Zaključek
Neskončni leni seznami
public static LazyList seqA() {
return new Nonempty(1, new F0() {
public LazyList apply() {
return seqA();
}
});
}
-
Uvod Funkcije Seznami Leni seznami Zaključek
Neskončni leni seznami
• [1, 1, 1, . . .]:
public static LazyList seqA() {
return new Nonempty(1, new F0() {
public LazyList apply() {
return seqA();
}
});
}
-
Uvod Funkcije Seznami Leni seznami Zaključek
Neskončni leni seznami
• [1, 1, 1, . . .]:
public static LazyList seqA() {
return new Nonempty(1, new F0() {
public LazyList apply() {
return seqA();
}
});
}
public static LazyList seqB() {
return new Nonempty(1, new F0() {
public LazyList apply() {
return add(seqA(), seqB());
}
});
}
-
Uvod Funkcije Seznami Leni seznami Zaključek
Neskončni leni seznami
• [1, 1, 1, . . .]:
public static LazyList seqA() {
return new Nonempty(1, new F0() {
public LazyList apply() {
return seqA();
}
});
}
• [1, 2, 3, . . .]:
public static LazyList seqB() {
return new Nonempty(1, new F0() {
public LazyList apply() {
return add(seqA(), seqB());
}
});
}
-
Uvod Funkcije Seznami Leni seznami Zaključek
Neskončni leni seznami
public static LazyList seqC() {
return
new Nonempty( 0,
new F0() {
public LazyList apply() {
return new Nonempty( 1,
new F0() {
public LazyList apply() {
return add(seqC(), seqC().tail());
}
}
);
}
}
);
}
-
Uvod Funkcije Seznami Leni seznami Zaključek
Neskončni leni seznami
• Fibonaccijevo zaporedje:
public static LazyList seqC() {
return
new Nonempty( 0,
new F0() {
public LazyList apply() {
return new Nonempty( 1,
new F0() {
public LazyList apply() {
return add(seqC(), seqC().tail());
}
}
);
}
}
);
}
-
Uvod Funkcije Seznami Leni seznami Zaključek
Zaporedje približkov števila π
•
π
4= 1−
1
3+
1
5−
1
7+ . . .
-
Uvod Funkcije Seznami Leni seznami Zaključek
Zaporedje približkov števila π
•
π
4= 1−
1
3+
1
5−
1
7+ . . .
// successive approximations to pi
public static LazyList pi() {
return scale(partialSums(piSummands(1, 1)), 4);
}
// [1, -1/3, 1/5, -1/7, ...]
private static LazyList piSummands(final int n, final int sign) {
return new Nonempty(
sign * 1.0 / n,
new F0() {
public LazyList apply() {
return piSummands(n + 2, -sign);
}
});
}
-
Uvod Funkcije Seznami Leni seznami Zaključek
Zaporedje približkov števila π
• Zaporedje 4(1− 1/3 + 1/5− 1/7 + . . .) počasi konvergira k π
-
Uvod Funkcije Seznami Leni seznami Zaključek
Zaporedje približkov števila π
• Zaporedje 4(1− 1/3 + 1/5− 1/7 + . . .) počasi konvergira k π
• Eulerjeva pohitritev:
S ′n= Sn+2 −
(Sn+2 − Sn+1)2
Sn − 2Sn+1 + Sn+2
-
Uvod Funkcije Seznami Leni seznami Zaključek
Zaporedje približkov števila π
• Zaporedje 4(1− 1/3 + 1/5− 1/7 + . . .) počasi konvergira k π
• Eulerjeva pohitritev:
S ′n= Sn+2 −
(Sn+2 − Sn+1)2
Sn − 2Sn+1 + Sn+2
public static LazyList euler(final LazyList list) {
double s0 = list.get(0).doubleValue();
double s1 = list.get(1).doubleValue();
double s2 = list.get(2).doubleValue();
return new Nonempty(
s2 - ((s2 - s1) * (s2 - s1)) / (s0 - 2 * s1 + s2),
new F0() {
public LazyList apply() {
return euler(list.tail());
}
});
}
-
Uvod Funkcije Seznami Leni seznami Zaključek
Zaporedje približkov števila π
• Leni seznam lenih seznamov:• prvi seznam je izvorni seznam približkov• drugi seznam je Eulerjeva pohitritev prvega• tretji seznam je Eulerjeva pohitritev drugega• . . .
-
Uvod Funkcije Seznami Leni seznami Zaključek
Zaporedje približkov števila π
• Leni seznam lenih seznamov:• prvi seznam je izvorni seznam približkov• drugi seznam je Eulerjeva pohitritev prvega• tretji seznam je Eulerjeva pohitritev drugega• . . .
public static LazyList eulerListOfLists(
final LazyList list) {
return new Nonempty(
list,
new F0() {
public LazyList apply() {
return eulerListOfLists(euler(list));
}
});
}
• Zaporedje sestavimo iz prvih členov posameznih seznamov
-
Uvod Funkcije Seznami Leni seznami Zaključek
Zaključek
• Vrednost lenih seznamov:
• elegantna formulacija neskončnih zaporedij• enkapsulacija stanja (npr. zaporedja naključnih števil)
-
Uvod Funkcije Seznami Leni seznami Zaključek
Zaključek
• Vrednost lenih seznamov:
• elegantna formulacija neskončnih zaporedij• enkapsulacija stanja (npr. zaporedja naključnih števil)
• Java kot funkcijski jezik?
• Omogoča implementacijo nekaterih funkcijskih konceptov• Okorna in dolgovezna sintaksa• Pomanjkanje operacij za delo s seznami• Pedagoška vrednost
UvodFunkcijeSeznamiLeni seznamiZaklju"cek