Rozbicie wykonania funkcji n argumentowej na n wykonań funkcji jednoargumentowych wyższego rzędu.
f: (T₁ × T₂ × T₃) → Tᵣ
fᶜ: T₁ → T₂ → T₃ → Tₙ = gᶜ
gᶜ: T₂ → T₃ → Tᵣ = hᶜ
hᶜ: T₃ → Tᵣ
f(x,y,z) ⇔ fᶜ(x)(y)(z)
Odwrotność względem currying.
fᶜ(x)(y)(z) ⇒ f(x,y,z)
Wykonanie funkcji wieloargumentowych z użyciem tylko części argumentów (aka częściowa aplikacja).
f: (x,y,z) ⇒ f (x)(y,z)
W praktyce terminy używane zamiennie.
val f(x: Int, y: Int) = x + y val g = f(13, _:Int)
val debug = (level: String, string: String) => "[" + level + "]: " + string val warning = debug("Warning", _: String) val error = debug("Error", _: String)
def method(x: Int, y: Int)(z: Int, v: Int) = x * y + z * v def f(x: Int, y: Int) = x + y val g = f(13, _:Int)
Funkcja której wolne zmienne zostały zamknięte (bound).
val y = 5 val f = (x: Int) => x + y f(14228, 5)
Wolne zmienne w funkcjach wyższego rzędu. Funkcja przenosi swój kontekst.
val outer = (z: Int) => { val y = 5 + z val f = (x: Int) => x + y f } val g = outer(5) g(3)
var x = 1 f = () => x x = 2 f()
var f: () => Int = _ var l: List[() => Int] = List() var i = 0 while (i <= 9) { f = () => i l = f :: l i = i + 1 } l(5)()
var f: () => Int = _ var l: List[() => Int] = List() for (i <- 0 to 9) { f = () => i l = f :: l } l(5)()
val pf: Int => Int = {case x => x + 1}
val pf: Int => Int = { case x if x > 0 => x + 1 case 0 => 0 case x => -x + 1 } pf(1) 1 match pf
val pf: Int => Int = {case x if x > 0 => x + 1} pf(1) pf(-1) #scala.MatchError: -1 (of class java.lang.Integer)
Dekompozycja argumentów
val pf: ((Int, Int)) => Int = {case (x, y) => x + y} val p = (2,2) pf(p) List(2->2, 1->1, 3->3) map pf List(2->2, 1->1, 3->3) map {case (x, y) => x + y}
Leniwa ewaluacja (lazy evaluation): obliczanie wyrażeń w najpóźniejszym możliwym momencie. Przeciwieństwo: eager evaluation, obliczanie wyrażeń w momencie deklaracji.
val x = { println("initializing x!"); 5 } x lazy val x = { println("initializing x!"); 5 } x
Przekazywanie argumentów przez wartość (call by value) vs przekazywanie wartości przez referencję (call by name):
def callByValue(x: Int) = { println("returning x!"); x; x } def callByName(x: => Int) = { println("returning x!"); x; x } callByValue({ println("initializing x!"); 5 }) callByName({ println("initializing x!"); 5 })
Wygoda, efektywność (zapobieganie wywołań), nieskończone listy.
lazy val now = new Date val start = System.currentTimeMillis lazy val end = System.currentTimeMillis // ... val total = end - start
var currentLevel = 2 def debug(level: Int, msg: => String) = if (currentLevel <= level) println(msg) debug(1, "Potentially bad things happening in thread " + Thread.currentThread) debug(2, "Potentially bad things happening in thread " + Thread.currentThread)
Leniwe strumienie to listy zaimplementowane za pomocą leniwej strategii ewaluacji, pozwalające na pracę na nieskończonych ciągach.
val s: (Int => List[Int]) = k => k :: s(k + 1) val s: (Int => Stream[Int]) = k => Stream.cons(k, s(k + 1)) // cons = construct from Lisp val naturals = s(1) naturals(100) naturals(101) val even = naturals.filter((k: Int) => k % 2 == 0) even(100) even(101)
- leniwej ewaluację wyrażenia,
- currying, uncurrying,
- przekazania argumentu call by name,
- nieskończone listy.
Zaimplementuj w Scali następujący generator code highlighterów (jakkolwiek to się nazywa po polsku).
Code highlighter to parser jakiegoś języka L działający wg jakiejś konfiguracji C. Ten parser dostaje na wejście kod źródłowy P jakiegoś programu napisanego w L i zwraca "pokolorowany" kod źródłowy P'.
Kod pokolorowany to taki, do którego dodane zostały znaczniki określające sposób prezentacji poszczególnych fragmentów tego kodu (np. à la HTML/CSS, e[31m...e[0m, itp.).
Sposób w jaki kod ma być pokolorowany jest określone przez C (np. słowa kluczowe na czerwono, boldface, operatory na niebiesko).
Czyli highlighter to funkcja P -> P' której semantykę określają L i C.
Generator code highlighterów dostaje na wejście specyfikację L i C i na wyjściu zwraca odpowiedni code highlighter.
Na potrzeby zadania wystarczą proste parser które rozróżniają tylko słowa kluczowe, operatory, typy, literały i "wszystko inne" danego języka.
Przykładowa specyfikacja mojego wymyślonego na szybko języka:
keywords={let, in, if, then, else, function} types={bool} literals={true, false, _} operators={∧, ∨, ¬, =} /*wszystko oddzielone spacjami od siebie w programie*/
Przykładowa konfiguracja:
keywords=black types=italic, blue literals=red operators=black other=gray
Przykładowy program:
let x = true in if ¬ x ∨ false then true else false
Space | Forward |
---|---|
Right, Down, Page Down | Next slide |
Left, Up, Page Up | Previous slide |
P | Open presenter console |
H | Toggle this help |