Groovy: funkcje i domknięcia

Slides:



Advertisements
Podobne prezentacje
Funkcje w PHP ABK.
Advertisements

Wstęp do strumieni danych
C++ wykład 2 ( ) Klasy i obiekty.
Język C/C++ Funkcje.
Programowanie obiektowe
Rekurencja 1 Podprogram lub strukturę danych nazywamy rekurencyjną, (recursive subprogram, recursive data structure) jeżeli częściowo składa się z samej.
Deklaracje i definicje klas w C++ Składowe, pola, metody Konstruktory
Programowanie obiektowe
Wzorce.
Język ANSI C Funkcje Wykład: Programowanie komputerów
Prowadzący: mgr inż. Elżbieta Majka
PROGRAMOWANIE STRUKTURALNE
formatowanie kodu źródłowego
Materiały do zajęć z przedmiotu: Narzędzia i języki programowania Programowanie w języku PASCAL Część 7: Procedury i funkcje © Jan Kaczmarek.
Materiały do zajęć z przedmiotu: Narzędzia i języki programowania Programowanie w języku PASCAL Część 8: Wykorzystanie procedur i funkcji © Jan Kaczmarek.
Bartosz Walter Inżynieria oprogramowania Lecture XXX JavaTM – część II Bartosz Walter
Kurs Pascala – spis treści
Struktury.
1 Dygresja: cztery płyty główne…. 2 Dygresja: osobliwości C /* cos o nieistniejacym typie Boolean */ /* oraz o operatorze przecinkowym */ #include int.
C++ wykład 2 ( ) Klasy i obiekty.
Tablice jednowymiarowe 1
Komunikacja z arkuszem. Iteracje. Funkcje.
Języki programowania obiektowego
Czytanie, pisanie i rysowanie – cd.. Jeszcze jeden strumyk PrintStream działa jak PrintWriter, ale: Używa domyślnego (systemowego) kodowania Nie wyrzuca.
Podprogramy.
Podstawy programowania
Podstawy programowania II
Podstawy programowania
Procedury i funkcje.
PHP: warunki, pętle, switch, break, continue
Jerzy F. Kotowski1 Informatyka I Wykład 8 STRUKTURA PROGRAMU n Funkcje n Klasy zmiennych n Projekt.
1 Wykład 8 Podprogramy. 2 Pojęcie i istota stosowania dzielenie programu na części (logicznie spójne) - nazwane - niezależne od pozostałych części - z.
Programowanie strukturalne i obiektowe
Jerzy F. Kotowski1 Informatyka I Wykład 14 DEKLARATORY.
JAVA c.d.. Instrukcji wyboru SWITCH używamy, jeśli chcemy w zależności od wartości pewnego wyrażenia wykonać jeden z kilku fragmentów kodu. Jest to w.
STEROWANIE Ale nie tylko
Andrzej Repak Nr albumu
Java – coś na temat Klas Piotr Rosik
Inicjalizacja i sprzątanie
Programowanie obiektowe Wykład 3 dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 1/21 Dariusz Wardowski.
Przekazywanie parametrów do funkcji oraz zmienne globalne i lokalne
Wykład 10 typ zbiorowy rekurencja.
PL/SQL – dalsza wędrówka
Programowanie strukturalne i obiektowe C++
Wydział Elektroniki Kierunek: AiR Zaawansowane metody programowania Wykład 5.
Technologie internetowe Wykład 5 Wprowadzenie do skrytpów serwerowych.
Programowanie obiektowe Wykład 9 dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 1/15 Dariusz Wardowski.
Programowanie proceduralne Podstawy Programowania dla geoinformatyków Wykład 3 Rafał Witkowski, 2015.
Haskell. Dopasowanie do wzorca Jest to operacja, gdzie pewnie wyrażenie sprawdza się ze wzorcem, w którym może znajdować się jedno lub więcej "wolnych.
Wykład 2 Programowanie obiektowe. Programowanie obiektowe wymaga dobrego zrozumienia działania funkcji definiowanych przez użytkownika, w ten sposób będziemy.
Pętle – instrukcje powtórzeń
Seminarium Dyplomowe: Metodyka i Techniki Programowania Autor: Bartłomiej Fornal.
Podstawy informatyki Funkcje Łukasz Sztangret Katedra Informatyki Stosowanej i Modelowania Prezentacja przygotowana w oparciu o materiały Danuty Szeligi.
Pakiety numeryczne Skrypty, funkcje Łukasz Sztangret Katedra Informatyki Stosowanej i Modelowania.
© Krzysztof Barteczko, PJWSTK 2012
do programowania obiektowego w języku Groovy
Podstawy informatyki Mechanizm obsługi sytuacji wyjątkowych Łukasz Sztangret Katedra Informatyki Stosowanej i Modelowania Prezentacja przygotowana w oparciu.
1 Kolekcje i tablice © Krzysztof Barteczko, PJWSTK 2009.
K URS JĘZYKA C++ – WYKŁAD 3 ( ) Przenoszenie Składowe statyczne Funkcje wbudowane Argumenty domyślne.
W pełni obiektowyInterpretowany i kompilowany Kompilowany do JVM Bytecode Umożliwia korzystanie z bibliotek Java Skonstruowany z myślą o wysokiej produktywności.
Lua - wprowadzenie ● lua.org – źródła, dokumentacja, podręcznik itp ● Interpreter - Lua.org->Downloads->Binaries->get a binary-> Windows->Wersja >Executables->
Programowanie Obiektowe – Wykład 6
Strumienie, Wczytywanie, Zapisywanie, Operacje na plikach
Kurs języka C++ – wykład 3 ( )
Delegaty Delegat to obiekt „wiedzący”, jak wywołać metodę.
Klasy wewnętrzne. Praktyka użycia interfejsów i klas wewnętrznych
Kurs języka C++ – wykład 13 ( )
Dane, zmienne, instrukcje
Haskell Składnia funkcji.
Zapis prezentacji:

Groovy: funkcje i domknięcia © Krzysztof Barteczko, PJWSTK 2012 1

Funkcje Funkcje definiujemy za pomocą składni: (def | Typ) funcName (parameters) { // nagłówek // ciało funkcji = instrukcje składające się na funkcję } i wywołujemy: funcName(arguments) W programowaniu obiektowym funkcje nazywamy metodami. W odniesieniu do definicji zawartych w prostych skryptach (bez definicji klas) będziemy stosować nazwę funkcja. 2

Wywołanie i wynik funkcji Wywołanie funkcji polega na przekazaniu sterowania do kodu ciała funkcji. Na skutek wykonania tego kodu może powstać jakaś wartość i może ona być zwrócona do miejsca wywołania. result = func() def func() { a = 2*10 return a } def func() { a = 2*10 } int func() { a = 2*10 return a } Funkcja może nie mieć żadnego wyniku. Wtedy jej typem wyniku jest Void. def hello() { println 'Hello' } void hello() { println 'Hello' } hello() Użycie def w miejsce typu wyniku oznacza dowolny typ. Wykonanie funkcji kończy się i sterowanie jest zwracane w miejsce wywołania gdy zakończy się ciało funkcji lub wykonana zostanie instrukcja return. Wynikiem funkcji jest wartość ostatniego wyrażenia wykonanego w ciele funkcji lub wartość wyrażenia podanego w instrukcji return. 3

println 'Groovy'  println('Groovy') Parametry i argumenty Jeżeli funkcja (metoda) ma parametry, to w jej wywołaniu można pominąć nawiasy okragłe (jeśli składnia jest jednoznaczna) println 'Groovy'  println('Groovy') Wywołanie metod getNnn i setNnn z klasy Bean może być zastąpione bezpośrednim operowaniem na właściwościach: s = f.getText()  s = f.text f.text = 'Ala ma kota' 4

Funkcje ze zmienną liczbą argumentów Definicja: def fun(... args) / def fun(def ... args) / String fun(int ... args) Przykład - definicja, wywołanie i dostęp do argumentów: vaf(1,2,5) vaf('Dog', 10, 'Cat', 5) def vaf( ... args) { s = args.size() println "Args num: $s" println 'First : ' + args[0] println 'Last : ' + args[s-1] for (a in args) { print a + ' ' } println() Args num: 3 First : 1 Last : 5 1 2 5 Args num: 4 First : Dog Dog 10 Cat 5 5

Argumenty domyślne Przy definicji funkcji jednemu lub kilku ostatnim (albo i wszystkim) argumentom można nadac wartości domyślne. rect() rect('A') rect('x',3,3) def rect(c='*', n=2, m=2) { for (i in 1..n) { for (j in 1..m) { print c } println() ** AA xxx 6

Nazwane parametery oo ***** XXXXXXX Funkcje z jednym parametrem typu Map można wywoływać podając nazwane parametry: id1 : val1, id2 : val2, ... Wygoda: nie trzeba podawać wszystkich, nie trzeba pamiętać pozycji rect char: 'o' rect cols: 5, rows: 1 rect cols: 7, char: 'X' def rect(amap) { if (!amap) println 'Invalid arg' def c = amap.char ?: '*' def n = amap.rows ?: 2 def m = amap.cols ?: 2 for (i in 1..n) { for (j in 1..m) print c println() } oo ***** XXXXXXX 7

Wiązania skryptu Zmienne wprowadzane w skrypcie i jego funkcjach bez nazwy typu (w tym bez def) są dodawane do wiązań skryptu i dostępne od momentu utworzenia również w innych funkcjach skryptu. a = 1 b = 2 // a i b dodane do wiązań (bindings) func1() println "$a $b" def func1() { println "$a $b" // te same a i b - pobrane z bindings a = 10; b = 11; } Wynik: 1 2 10 11 Bindings zapewniają również komunikację pomiędzy różnymi skryptami, np. ze skryptami uruchamianymi dynamicznie w trakcie wykonania programu. 8

Zmienne lokalne Zmienne wprowadzane z nazwą typu lub słowem def są zmiennymi lokalnymi, widocznymi tylko w danym bloku (i blokach w nim zawartych). def c = 100 def d = 111 func2() println "$c $d" def func2() { // println "$c $d" // Błąd wykonania: nieznane zmienne c i d def c = 77, d = 88 // Nowe, lokalne dla tego bloku c i d } Wynik: 77 88 100 111 To są różne bloki! W bardziej skomplikowanych skryptach warto używać def, aby przypadkowo nie popsuć wartości jakichś zmiennych 9

{ [closureArguments->] statements } Domknięcia Domknięcie jest fragmentem kodu - anonimową funkcją, która może mieć argumenty, zwraca wynik i ma dostęp do wszystkich zmiennych widocznych w otaczającym kontekście (klasie, metodzie, blokach). { [closureArguments->] statements } Tak zapisywane fragmenty kodu można przypisywać zmiennym, przekazywać funkcjom (metodom) i zwracać z funkcji (metod). def add = { x, y -> return x + y } def mul = { x, y -> return x*y } println add(1,5) println add.call(1,5) println op( 7, 8, add) println op(7, 8, mul) def op(x, y, closure) { return closure(x, y) } 6 15 56 10

Argumenty domknięć (1..N). Jednoargumentowe domknięcia nie wymagają deklarowania parametru, a wartość przekazanego argumentu dostępna jest w specjalnej zmiennej it. Jeśli wołanie bez arg -> parametr = null. def ok = { println 'Ok' } def p = { println it } def pow = { x,y=2 -> println x**y } def sum = { ... num -> sum = 0 for (n in num) sum += n println sum } def info = { map -> for (en in map) println "${en.key}: ${en.value}" ok() p 'Groovy' pow 2 pow 2,4 sum 1,2,3 sum 10,11 info Language: 'Groovy', version: '1.7' Ok Groovy 4 16 6 21 Language: Groovy version: 1.7 11

Wyniki domknięc Wykonanie domknięcie zawsze zwraca wynik albo na skutek wykonania instrukcji return albo jako wynik ostatniej wykonanej w ciele domknięcia instrukcji. Jeśli wynikiem jest void zwracana jest wartość null. def dbl = { it*2 } def max = { x, y -> x > y ? x : y } def p = { println it } def c = { if (it > 10) return; it } println dbl(2) println max(1,100) println p('Some text') println c(11) println c(7) 4 100 Some text null 7 12

Przekazywanie domknięć funkcjom Domknięcie można zdefiniować (zadeklarować) ad hoc bezpośrednio na liście argumentów wywołania funkcji (metody). Jeśli jest ostatnim argumentem to można zastosować wygodną składnię. func(arg1, arg2, ... argN) { closure_body } def x = 0 def incr = { x++ } op(3, incr) println x op(3, { x++ } ) op(3) { x++ } def op(n, closure) { for(i in 1..<n) closure() return closure() } 3 6 9 13

Kontekst (zakres) domknięcia Zakres domknięcia - widoczne w momencie deklaracji zmienne (i inne identyfikatory) z otaczających bloków. W momencie deklaracji następuje związanie tych zmiennych z domknięciem, a przy wykonaniu domknięcie ma żywy dostęp do ich aktualnych w momencie wykonania wartości i może je zmieniać. def x = 0 def incr = { x++ } println x // 0 x = 10 println x incr() println x // 11 Moment deklaracji. Kod nie jest wykonywany Moment wykonania. Domknięcie zna aktualną wartość x i może ją zmienić. 14

Zasadnicza różnica! def x = 0 op(10) { x++ } println x // 10 def op(n, closure) { // println x for(i in 1..<n) closure() return closure() } Funkcja nic nie wie o x (błąd na czerwono) Ani o tym co robi domknięcie. Domknięcie ma dostęp do x i jego aktualnych wartości i może je zmieniać 15

Domknięcia: iteracje liczbowe def txt = '' 7.times { txt += "Groovy $it\n" } print txt char c = 'A' 1.upto(10) { print c++ } println() def z = '*' 5.downto(1) { println z * it Groovy 0 Groovy 1 Groovy 2 Groovy 3 Groovy 4 Groovy 5 Groovy 6 ABCDEFGHIJ ***** **** *** ** * Operacja duplikacji (powtórzenia) napisu 16

Domknięcia: iteracje na obiektach Dla każdego obiektu, dopuszczającego iteracje są metody: each (Closure) // it == kolejny element eachWithIndex (Closure) // elt, index -> Liczba arg. zależy od def. domknięcia 123 [2, 4, 6] x 100 y 200 w 300 h 500 x = 100 y = 200 w = 300 h = 500 [0] x=100 [1] y=200 [2] w=300 [3] h=500 (0) x = 100 (1) y = 200 (2) w = 300 (3) h = 500 [G, r, o, o, v, y] 1 2 3 4 5 8 / 12 13 14 15 def list = [1,2,3] list.each { print it } println() list.eachWithIndex { e, i -> list[i] *= 2 } println list def map = [x: 100, y: 200, w: 300, h: 500 ] map.each { println it.key + ' ' + it.value } map.each { k, v -> println "$k = $v" } map.eachWithIndex { entry, i -> println "[$i] $entry" } map.eachWithIndex { k, v, i -> println "($i) $k = $v" } String s = 'Groovy' def outList = [] s.each { outList << it } println outList (1..5).each { print it + ' ' } def d1 = new Date(), d2 = d1 + 3 print '\n' + (d1.month+1) + ' /' (d1..d2).each { print ' ' + it.date } 17

Domknięcia jako klasyfikatory w switch def list2 = [] def list3 = [] def other = [] (1..21).each { num -> switch(num) { case { it%2 == 0 } : list2 << num; break case { it%3 == 0 } : list3 << num; break default: other << num } println "Multiple of 2: $list2" println "Multiple of 3: $list3" println "Other: $other" //Wynik: Multiple of 2: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] Multiple of 3: [3, 9, 15, 21] Other: [1, 5, 7, 11, 13, 17, 19] Domknięcie jako klasyfikator 18

Domknięcia: nie ma globalnego return list = 'a b c d '.tokenize() func1() func2() def func1 () { list.each { if (it == 'b') return else println it } // Workaround def func2 () { try { if (it == 'b') throw new Exception() } catch(Exception exc) { return } a c d 19

Przekształcanie funkcji w domknięcia [def] var = &func_name def function(arg) { println arg } def f = this.&function f('A') def sysprop = System.&getProperty println sysprop('file.separator') Zatem możemy normalne funkcje (metody) przekazywać jako argumenty i zwracać jako wyniki. 20

Zwijanie domknięć - currying Przekształcenie funkcji F:XxY → R w funkcję G: X → (Y → R) closure.curry(a1, a2, .. aN) daje domknięcie z ustalonymi pierwszymi N argumentami. closure.rcurry(a1, a2, .. aN) daje domknięcie z ustalonymi ostatnimi N argumentami. closure.ncurry(P, a1, a2, .. aN) daje domknięcie z ustalonymi N argumentami poczynając od pozycji P na liście argumentów // Groovy release notes // left currying def multiply = { a, b -> a * b } def doubler = multiply.curry(2) println doubler(10) // right currying def divide = { a, b -> a / b } def halver = divide.rcurry(2) println halver(20) // currying n-th parameter def joinWithSeparator = { one, sep, two -> one + sep + two } def joinWithComma = joinWithSeparator.ncurry(1, ', ') println joinWithComma('a', 'b') 21

Currying = poproszę funkcję (1) def tagIt = { data, tagKind -> "<$tagKind>$data<\\$tagKind>" } def bold = tagIt.rcurry('bold') def italic = tagIt.rcurry('italic') println bold("Kowalski") + italic("Jan") 22

Currying = poproszę funkcję (1) def koszt = String.&format.curry('%.2f') def data = String.&format.curry('%tF') def dzis = new Date() println 'Data zakupu ' + data(dzis) + ', koszt: ' + koszt(3.2) 23

Kompozycja domknięć Funkcja złożona: f(g(h(x))) // from release notes def plus2 = { it + 2 } def times3 = { it * 3} def times3plus2 = plus2 << times3 def plus2times3 = times3 << plus2 println times3plus2(2) println plus2times3(2) times3plus2 = times3 >> plus2 // wynik 8 12 24

Currying + kompozycja def tagIt = { data, tagKind -> "<$tagKind>$data<\\$tagKind>" } def bold = tagIt.rcurry('bold') def italic = tagIt.rcurry('italic') def bi = italic >> bold println bi("Kowalski Jan") <bold><italic>Kowalski Jan<\italic><\bold> 25

Currying + kompozycja def discount = 0.1 def rate = 0.2 def multiply = { x, y -> return x * y } def priceWithTax = multiply.curry(1 + rate) def priceWithDiscount = multiply.curry(1 - discount) def netPrice = priceWithDiscount << priceWithTax println netPrice(100) 26

Trampolina „When writing recursive algorithms, you may be getting the infamous stack overflow exceptions, as the stack starts to have a too high depth of recursive calls. An approach that helps in those situations is by using Closures and their new trampoline capability. Closures are wrapped in a TrampolineClosure. Upon calling, a trampolined Closure will call the original Closure waiting for its result. If the outcome of the call is another instance of a TrampolineClosure, created perhaps as a result to a call to the trampoline() method, the Closure will again be invoked. This repetitive invocation of returned trampolined Closures instances will continue until a value other than a trampolined Closure is returned. That value will become the final result of the trampoline. That way, calls are made serially, rather than filling the stack.” Groovy release notes To się też nazywa: „Declarative tail-call optimization” (rekurencja ogonowa) 27

Trampolina - przykład z rel. notes. def factorial1 def factorial2 factorial1 = { int n, def accu = 1G -> if (n < 2) return accu factorial1(n - 1, n * accu) } factorial2 = { int n, def accu = 1G -> factorial2.trampoline(n - 1, n * accu) // działa jak curry } // zwraca TrampolinedClos factorial2 = factorial2.trampoline() println factorial2(1000) println factorial1(1000) Kluczowe: rekurencja ogonowa, czyli ostatnia operacja w funkcji wywołanie samej siebie lub zwrócenie wyniku 28

Memoizacja (memoization) The return values for a given set of Closure parameter values are kept in a cache, for those memoized Closures. That way, if you have an expensive computation to make that takes seconds, you can put the return value in cache, so that the next execution with the same parameter will return the same result – again, we assume results of an invocation are the same given the same set of parameter values. There are three forms of memoize functions: the standard memoize() which caches all the invocations memoizeAtMost(max) call which caches a maximum number of invocations memoizeAtLeast(min) call which keeps at least a certain number of invocation results and memoizeBetween(min, max) which keeps a range results (between a minimum and a maximum) 29

Memoizacja - przykład 30 long start long count = 0 def startTimer = { -> start = System.currentTimeMillis() } def elapsed = { -> System.currentTimeMillis() - start } def fib fib = { n -> count++ if (n < 2) n else fib(n - 1) + fib(n - 2) } startTimer() println fib(34) println 'Czas = ' + elapsed()/1000 + ' sek. Wolań: ' + count fib = fib.memoize() count = 0 5702887 Czas = 15.125 sek. Wolań: 18454929 Czas = 0 sek. Wolań: 35 30