54
Uvod Funkcije Seznami Leni seznami Zakljuˇ cek Funkcije, neskonˇ cna zaporedja in java LALGinar, 4. oktober 2013 Luka F¨ urst

Funkcije, neskonˇcna zaporedja in javalalg.fri.uni-lj.si/~uros/LALGinar/arhiv/funkcijsko_java.pdf · Uvod Funkcije Seznami Leni seznami Zakljuˇcek Funkcijsko programiranje • Funkcije

  • Upload
    others

  • View
    2

  • Download
    0

Embed Size (px)

Citation preview

  • 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