Przygotował: Jacek Sroka. PO* Scala c.d. przygotował Jacek Sroka w oparciu o materiały Martina Oderskiego

Podobne dokumenty
Programowanie obiektowe

JAVA W SUPER EXPRESOWEJ PIGUŁCE

Programowanie obiektowe

Kurs programowania. Wykład 1. Wojciech Macyna. 3 marca 2016

Scala. Obiektowo-funkcyjny język programowania. Zbyszek Skowron

Platformy Programistyczne Podstawy języka Java

Programowanie obiektowe

Język programowania Scala / Grzegorz Balcerek. Wyd. 2. Poznań, cop Spis treści

Języki Programowania II Wykład 3. Java podstawy. Przypomnienie

Aplikacje Internetowe. Najprostsza aplikacja. Komponenty Javy. Podstawy języka Java

Kurs programowania. Wykład 2. Wojciech Macyna. 17 marca 2016

Klasy abstrakcyjne, interfejsy i polimorfizm

Java Język programowania

Kurs programowania. Wykład 13. Wojciech Macyna. 14 czerwiec 2017

Dziedziczenie. dr Jarosław Skaruz

Java: kilka brakujących szczegółów i uniwersalna nadklasa Object

Enkapsulacja, dziedziczenie, polimorfizm

Polimorfizm. dr Jarosław Skaruz

Programowanie obiektowe

KOTLIN. Język programowania dla Androida

Laboratorium 03: Podstawowe konstrukcje w języku Java [2h]

Polimorfizm, metody wirtualne i klasy abstrakcyjne

Wykład 2 Wybrane konstrukcje obiektowych języków programowania (1)

Java - tablice, konstruktory, dziedziczenie i hermetyzacja

Programowanie obiektowe

Kurs WWW. Paweł Rajba.

Definiowanie własnych klas

Programowanie obiektowe

Informacje ogólne. Karol Trybulec p-programowanie.pl 1. 2 // cialo klasy. class osoba { string imie; string nazwisko; int wiek; int wzrost;

Programowanie obiektowe

Wykład 6: Dziedziczenie

Obszar statyczny dane dostępne w dowolnym momencie podczas pracy programu (wprowadzone słowem kluczowym static),

Programowanie obiektowe i zdarzeniowe

Język JAVA podstawy. Wykład 3, część 3. Jacek Rumiński. Politechnika Gdańska, Inżynieria Biomedyczna

Kurs programowania. Wykład 3. Wojciech Macyna. 22 marca 2019

Programowanie w Internecie. Java

Podstawowe części projektu w Javie

Dokumentacja do API Javy.

Programowanie obiektowe

Wykład 4: Klasy i Metody

Java. język programowania obiektowego. Programowanie w językach wysokiego poziomu. mgr inż. Anna Wawszczak

Informatyka I. Dziedziczenie. Nadpisanie metod. Klasy abstrakcyjne. Wskaźnik this. Metody i pola statyczne. dr inż. Andrzej Czerepicki

Wykład 7: Pakiety i Interfejsy

Programowanie i projektowanie obiektowe

Akademia ETI. Wprowadzenie do programowania w Javie PG Java User Group Przemysław Kulesza

Swift (pol. jerzyk) nowy język programowania zaprezentowany latem 2014 r. (prace od 2010 r.)

Składnia C++ Programowanie Obiektowe Mateusz Cicheński

Technologie i usługi internetowe cz. 2

Języki i metody programowania Java INF302W Wykład 2 (część 1)

Abstrakcyjny typ danych

Programowanie obiektowe

Kurs programowania. Wykład 9. Wojciech Macyna. 28 kwiecień 2016

1. Które składowe klasa posiada zawsze, niezależnie od tego czy je zdefiniujemy, czy nie?

Kurs programowania. Wstęp - wykład 0. Wojciech Macyna. 22 lutego 2016

Wstęp do programowania

Programowanie w Javie 1 Wykład i Ćwiczenia 3 Programowanie obiektowe w Javie cd. Płock, 16 października 2013 r.

Programowanie w Javie wykład 8 Interfejsy

Podstawy Programowania Obiektowego

Laboratorium z przedmiotu: Inżynieria Oprogramowania INEK Instrukcja 6

dziedziczenie - po nazwie klasy wystąpią słowa: extends nazwa_superklasy

Wstęp do ruby dla programistów javy

Informatyka I. Klasy i obiekty. Podstawy programowania obiektowego. dr inż. Andrzej Czerepicki. Politechnika Warszawska Wydział Transportu 2018

1 Atrybuty i metody klasowe

Programowanie obiektowe - 1.

Scala - programowanie obiektowo-funkcyjne

Swift (pol. jerzyk) nowy język programowania zaprezentowany latem 2014 r. (prace od 2010 r.)

PARADYGMATY PROGRAMOWANIA Wykład 4

Typy klasowe (klasy) 1. Programowanie obiektowe. 2. Założenia paradygmatu obiektowego:

Aplikacje w środowisku Java

Strona główna. Strona tytułowa. Programowanie. Spis treści. Sobera Jolanta Strona 1 z 26. Powrót. Full Screen. Zamknij.

Języki i metody programowania Java. Wykład 2 (część 2)

Klasa jest nowym typem danych zdefiniowanym przez użytkownika. Najprostsza klasa jest po prostu strukturą, np

Programowanie obiektowe

Programowanie strukturalne. Opis ogólny programu w Turbo Pascalu

Programowanie obiektowe

WSNHiD, Programowanie 2 Lab. 2 Język Java struktura programu, dziedziczenie, abstrakcja, polimorfizm, interfejsy

Składnia C++ Programowanie Obiektowe Mateusz Cicheński

Wstęp do programowania obiektowego. WYKŁAD 3 Dziedziczenie Pola i funkcje statyczne Funkcje zaprzyjaźnione, this

Programowanie obiektowe

Materiały do zajęć VII

Tworzenie aplikacji w języku Java

Laboratorium z przedmiotu: Inżynieria Oprogramowania INEK Instrukcja 7

Dziedziczenie. Streszczenie Celem wykładu jest omówienie tematyki dziedziczenia klas. Czas wykładu 45 minut.

KLASY, INTERFEJSY, ITP

JAVA. Java jest wszechstronnym językiem programowania, zorientowanym. apletów oraz samodzielnych aplikacji.

Klasy i obiekty cz II

UML a kod w C++ i Javie. Przypadki użycia. Diagramy klas. Klasy użytkowników i wykorzystywane funkcje. Związki pomiędzy przypadkami.

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

Programowanie RAD Delphi

Przygotował: Jacek Sroka. JNP 3 język Scala

Kurs programowania. Wykład 9. Wojciech Macyna

Wstęp do programowania

Marcin Luckner Politechnika Warszawska Wydział Matematyki i Nauk Informacyjnych

Java Programowanie Obiektowe Ćwiczenie 1- wprowadzenie

Na przykładzie języków Java, C# i odrobiny C++ Dariusz Brzeziński. Politechnika Poznańska, Instytut Informatyki

Programowanie w Javie - wykład 3

Klasy abstrakcyjne i interfejsy

Wprowadzenie do języka Java

Aplikacje w środowisku Java

Programowanie obiektowe, wykład nr 6. Klasy i obiekty

Transkrypt:

1 PO* Scala c.d. przygotował Jacek Sroka w oparciu o materiały Martina Oderskiego

2 Programowanie obiektowe vs programowanie funkcyjne Funkcje są pierwszoplanowym operatorem języka, można je traktować tak jak dane i na nich operować (składać w potężniejsze funkcje) P. obiektowe jest prostopadłe do p. funkcyjnego p. obiektowe uzupełnia (i imperatywne i funkcyjne i nawet w logice) W p. obiektowym globalny stan jest zły, w funkcyjnym w ogóle modyfikowanie stanu jest złe, bo to psuje prawa (teorie)

3 Funkcje wyższego rzędu Funkcje są normalnymi wartościami Można je przekazywać jako parametr i zwracać jako wynik Funkcje wyższego rzędu Suma liczb całkowitych między a i b def sumints(a: Int, b: Int): Int = if (a > b) 0 else a+sumints(a+1,b) Suma kwadratów liczb całkowitych między a i b def square(x:int): Int = x*x def sumsquares(a: Int, b: Int): Int = if (a > b) 0 else square(a)+sumsquares(a+1,b) Suma 2^n dla n od a do b def pt(x: Int): Int = if (x==0) 1 else 2*pT(x-1) def sumofpt(a: Int, b: Int): Int = if (a > b) 0 else pt(a)+sumofpt(a+1,b)

4 Bardziej ogólne rozwiązanie def sum(f: Int => Int, a: Int, b: Int): Int = if (a > b) 0 else f(a) + sum(f, a+1, b) def id(x: Int): Int = x def square(x: Int): Int = x*x def pt(x: Int): Int = if (x==0) 1 else 2*pT(x-1) def sints(a: Int, b: Int): Int = sum(id, a, b) def ssquares(a: Int, b: Int): Int = sum(square, a, b) def spt(a: Int, b: Int): Int = sum(pt, a, b)

5 Funkcje anonimowe Zamiast zaśmiecać przestrzeń nazw można używać wyrażeń, które wyliczają się do funkcji def s2ints(a: Int, b: Int) = sum((x: Int) => x, a, b) def s2squares(a: Int, b: Int) = sum((x: Int) => x*x, a, b) Typ parametru można pominąć, jeżeli kompilator da radę się go domyśleć def s3ints(a: Int, b: Int) = sum((x) => x, a, b) A jeżeli jest jeden parametr z oczywistym typem, to można pominąć nawiasy def s3squares(a: Int, b: Int) = sum(x => x*x, a, b)

6 Funkcje anonimowe to lukier syntaktyczny (ang. syntactic sugar) Rownoważne są (x1: T1,..., xn: Tn) => E {def f(x1: T1,..., xn:tn) = E; f _ //gdzie f jest nową nazwą _ oznacza potraktowanie niepełnego wywołania funkcji jako nowej funkcji (można je pomijać jeżeli w tym miejscu oczekiwano funkcji) def sum2(f : Int => Int): (Int, Int) => Int = { def sumf(a:int, b:int):int = if (a>b) 0 else f(a)+sumf(a+1, b) sumf //mamy mniej przepisywania parametrów a i b def ssquares = sum2(x=>x*x) ssquares(1,10) Aplikacja funkcji jest lewostronnie łączna (sum2(x=>x*x))(1,10) sum2(x=>x*x)(1,10)

7 Currying (wypromował Haskell B. Curry) Funkcje zwracające funkcje są tak przydatne, że jest dla nich specjalna składnia (znowu lukier) def sumc(f: Int => Int)(a: Int, b: Int): Int= if (a > b) 0 else f(a)+sumc(f)(a+1,b) def sints = sumc(x => x) _ def ssquares = sumc(x => x*x) _ def spt = sumc(pt) _ Faktycznie prawdziwa jest definicja rekurencyjna def f (args1)...(argsn) = E def f (args1)...(args(n-1)) = (argsn) => E lub def f (args1)...(args(n-1)) = {def g(argsn) => E; g... def f =(args1) =>... => (args(n)) => E

8 Przykład: punkty stałe val tolerance = 0.0001 def iscloseenough(x: Double, y: Double) = Math.abs((x-y)/x) < tolerance def fixedpoint(f: Double => Double)(firstGuess: Double) = { def iterate(guess: Double): Double = { val next = f(guess) if (iscloseenough(guess, next)) next else iterate(next) iterate(firstguess) przy pomocy takiej funkcji policzymy pierwiastek

9 Przykład: pierwiastek sqrt(x) to y taki że y*y=x, inaczej y=x/y def sqrt(x: Double) = fixedpoint(y=>x/y)(1.0) żeby uniknąć pętli (2.0 1.0 2.0 1.0 2.0 1.0...) trzeba wytłumić parametr guess (żeby nie zmieniał się tak szybko) def sqrt2(x: Double) = fixedpoint(y=>(y + x/y)/2)(1.0) A ponieważ takie tłumienie to dobry pomysł, można ogólniej def avgdamp(f: Double=>Double)(x: Double) = (x + f(x))/2 def sqrt3(x: Double) = fixedpoint(avgdamp(y=>x/y))(1.0)

10 Podsumowanie składni Program = ciąg znaków Unicode Znaki whitespace np., \t, \n letter a-z i A-Z digit 0-9 delimeter., ; ( ) { [ ] \ ' operator pozostałe znaki, np. # + : Dalej [] to opcja, a { to 0 lub wiele

Trochę gramatyki Leksemy ident = letter {letter digit operator { operator ident _ ident literal = jak w Javie, np. 0, 1.0e10, x, "he said \"hi!\"", true np. x Room10a + -- foldl_: +_vector do rozdzielania używamy spacji x+-y sparsuje się do x +- y trzeba dodać jakąś spację x+ -y $ jest zarezerwowany dla kompilatora Zarezerwowane abstract case catch class def do else extends false final finally for if implicit import match new null object override package private protected requires return sealed super this throw trait try true type val var while with yield _ : = => <<: <% >: # @ 11

12 Trochę gramatyki c.d. Typy Type = SimpleType FunctionType FunctionType = SimpleType => Type ( [Types] ) => Type SimpleType = Byte Short Char Int Long Float Double Boolean Unit String Types = Type {, Type

13 Trochę gramatyki c.d. Wyrażenia Expr = InfixExpr FunctionExpr if ( Expr ) Expr else Expr InfixExpr = PrefixExpr InfixExpr Operator InfixExpr Operator = ident PrefixExpr = [ + -! ~ ] SimpleExpr SimpleExpr = ident literal SimpleExpr. ident Block FunctionExpr = Bindings => Expr Bindings = ident [':' SimpleType] '(' [Binding {',' Binding] ')' Binding = ident [ : Type] Block = { {Def ; Expr

14 Trochę gramatyki c.d. Definicje Def = FunDef ValDef FunDef = def ident { ( [Parameters] ) [ : Type] = Expr ValDef = val ident [ : Type] = Expr Parameters = Parameter {, Parameter Parameter = ident : [ => ] Type

15 Liczby wymierne class Rational(n: Int, d: Int) {//na razie tylko jeden konstr. //wykonuje ciało!!! private def gcd(x: Int, y: Int): Int = { if (x == 0) y else if (x < 0) gcd(-x, y) else if (y < 0) gcd(x, -y) else gcd(y % x, x) private val g = gcd(n, d) val numer: Int = n/g //def się wylicza za każdym wywołaniem val denom: Int = d/g //tu mamy pole, a nie metodę def +(that: Rational) = new Rational(numer * that.denom + that.numer * denom, denom * that.denom) def -(that: Rational) = new Rational(numer * that.denom - that.numer * denom, denom * that.denom) def *(that: Rational) = new Rational(numer * that.numer, denom * that.denom) def /(that: Rational) = new Rational(numer * that.denom, denom * that.numer) def unary_- : Rational = new Rational(-numer, denom) //spacja między "- :" jest potrzebna

16 Kolejność operatorów decyduje pierwszy znak; poniżej w kolejności rosnącego priorytetu (dowolne litery) ^ & < > =! : + - * / % (wszystkie pozostałe znaki specjalne) np. a + b ^? c?^ d less a ==> b c //wybieramy kolejno najsilniejsze operatory ((a + b) ^? (c?^ d)) less ((a ==> b) c)

17 Metody bezparametrowe Odwołujemy się jak do pól class Rational(n: Int, d: Int) extends AnyRef {... def square = new Rational(numer*numer, denom*denom) val r = new Rational(3, 4) println(r.square) // wypisze 9/16 Różnica polega na momencie wyliczenia wyrażenia dla pola w chwili tworzenia obiektu dla metody chwili wywołania Wygoda w zmienianiu definicji klasy (pole może stać się metodą)

18 Tworzenie i używanie obiektów var i = 1 //to jest zmienna (por val i def) var x = new Rational(0, 1) while (i <= 10) { x += new Rational(1, i) i += 1 println("" + x.numer + "/" + x.denom)

19 Parametry konstruktorów var mutowalne; val niemutowalne public class Foo() { public Bar bar; public Foo(Bar bar) {this.bar = bar; class Foo(val bar:bar) class Foo(_bar: Bar) { val bar = _bar public class Foo() { private final Bar bar; public Foo(Bar bar) {this.bar = bar; class Foo(private val bar: Bar)

20 Parametry konstruktorów public class Foo() { private Bar bar; public Foo(Bar bar) {this.bar = bar; public Bar getbar() {return bar; public void setbar(bar bar) {this.bar = bar; class Foo(@BeanProperty var bar:bar)//ale pole będzie public class Foo(aBar:Bar) {//to samo ale bez publicznego pola @BeanProperty private var bar = abar

Przeciążone konstruktory class Person(val firstname: String, val lastname: String, val age: Int) { //wszystkie konstruktory wywołują w końcu konstruktor podstawowy /** * A secondary constructor. */ def this(firstname: String) { this(firstname, "", 0); println("\nno last name or age given.") //lub def this(firstname: String) = this(firstname, "", 0); /** * Another secondary constructor. */ def this(firstname: String, lastname: String) { this(firstname, lastname, 0); println("\nno age given.") override def tostring: String = { return "%s %s, age %d".format(firstname, lastname, age) 21

22 Dziedziczenie i przesłanianie Korzeniem hierarchii klas jest AnyRef class Rational(n: Int, d: Int) extends AnyRef { w implementacji Javowej to alias dla java.lang.object standardowe przedefiniowanie i dziedziczenie class Rational(n: Int, d: Int) extends AnyRef { override def tostring = "" + numer + "/" + denom Jak to w językach obiektowych podklasa może być używana wszędzie tam gdzie nadklasa var y: AnyRef = new Rational(1,2)

23 Dziedziczenie i przeciążone konstruktory class Student(firstName:String, lastname:string, age:int, val subject:string)//to val nie byłoby dozwolone dla //pozostałych parametrów, bo nie chcemy //powielać pól, które już były //(tu nie odróżniamy atrybutów/metody) extends Person(firstName, lastname, age) { def dosomething = { System.out.println("I'm studying hard, Ma, I swear! (Pass the beer, guys!)")

24 Liczby wymierne c.d. class Rational(n: Int, d: Int) { require(y!= 0, "denominator must be nonzero") //require(y > 0, "denominator must be pozitive") //... wyjątek java.land.illegalargumentexception: requirement failed: denominator must be nonzero

25 Klasy abstrakcyjne i traits przykład: zbiór liczb całkowitych abstract class IntSet { def incl(x: Int): IntSet def contains(x: Int): Boolean posiada składowe bez implementacji nie można tworzyć egzemplarzy traits to klasy abstrakcyjne, które można dodać do innych klas (coś jakby interfejs); ale nie mogą mieć parameterów (wartości przekazywanych do konstruktora) a odwołania do super jest są wyliczane dynamicznie więc można np. przy ich pomocy zrobić filtry (nie ma problemów z wielodziedziczeniem dzięki linearization) trait TestNależenia { def contains(x: Int): Boolean def czynależy(x: Int) = if (contains(x)) println("należy") else println("nienależy") class BST extends IntSet with TestNależenia with new BasicIntQueue with Incrementing with Filtering //mixin

26 Przykład: zbiory jako drzewa BST class EmptySet extends IntSet { def contains(x: Int): Boolean = false def incl(x: Int): IntSet = new NonEmptySet(x, new EmptySet, new EmptySet) class NonEmptySet(elem: Int, left: IntSet, right: IntSet) extends IntSet { def contains(x: Int): Boolean = if (x < elem) left contains x else if (x > elem) right contains x else true def incl(x: Int): IntSet = //pamiętamy wszystkie wersje tej kolekcji if (x < elem) new NonEmptySet(elem, left incl x, right) else if (x > elem) new NonEmptySet(elem, left, right incl x) else this

27 Dynamiczne wiązanie Wybór metody która ma być wywołana opiera się na rzeczywistym typie obiektu (new EmptySet).contains(7) -> (zastępujemy contains jej implementacją w EmptySet) false new NonEmptySet(7, new EmptySet, new EmptySet).contains(1) -> (zastępujemy contains jej implementacją w NonEmptySet) if (1 < 7) new EmptySet contains 1 else if (1 > 7) new EmptySet contains 1 else true -> (przepisanie instrukcji warunkowej) new EmptySet contains 1 -> (zastępujemy contains jej implementacją w EmptySet) false To jest podobne (inny rodzaj polimorfizmu) do wyboru funkcji jaka ma być wywołana przez funkcję wyższego rzędu, który jest dokonywany w chwili wywołania Scala reprezentuje funkcje jako obiekty

28 Obiekty (Singletony) Żeby nie tworzyć za często nowego egzemplarza EmptySet można zrobić sobie Singleton val EmptySetSingleton = new EmptySet niestety taka definicja w Scali nie jest dozwolona na najwyższym poziomie po co definiować klasę i singleton, w Scali można krócej object EmptySet extends IntSet { def contains(x: Int): Boolean = false def incl(x: Int): IntSet = new NonEmptySet(x, EmptySet, EmptySet) Obiekty są tworzone leniwie (w chwili pierwszego odwołania do składowej) wszystko tak jak w klasie, ale bez parametrów konstruktora

29 Klasy standardowe O wszystkich wartościach w Scali można myśleć jak o obiektach W module Predef, typy podstawowe są też zdefiniowane jako obiekty type boolean = scala.boolean type int = scala.int type long = scala.long... Kompilator optymalizuje operacje na typach podstawowych, ale semantycznie wszystko działa jak dla obiektów (uwaga na priorytety operacji)

30 Przykład: Boolean package scala abstract class Boolean { def && (x: => Boolean): Boolean def (x: => Boolean): Boolean def! : Boolean def == (x: Boolean) : Boolean def!= (x: Boolean) : Boolean def < (x: Boolean) : Boolean def > (x: Boolean) : Boolean def <= (x: Boolean) : Boolean def >= (x: Boolean) : Boolean

31 Hipotetyczna implementacja abstract class Boolean { def ifthenelse(thenpart: => Boolean, elsepart: => Boolean) def && (x: => Boolean): Boolean = ifthenelse(x, false) def (x: => Boolean): Boolean = ifthenelse(true, x) def! : Boolean = ifthenelse(false, true) def == (x: Boolean) : Boolean = ifthenelse(x, x.!) def!= (x: Boolean) : Boolean = ifthenelse(x.!, x) def < (x: Boolean) : Boolean = ifthenelse(false, x) def > (x: Boolean) : Boolean = ifthenelse(x.!, false) def <= (x: Boolean) : Boolean = ifthenelse(x, true) def >= (x: Boolean) : Boolean = ifthenelse(true, x.!) case object True extends Boolean { def ifthenelse(t: => Boolean, e: => Boolean) = t case object False extends Boolean { def ifthenelse(t: => Boolean, e: => Boolean) = e

32 Przykład: Int package scala abstract class Int extends AnyVal { def tolong: Long def tofloat: Float def todouble: Double def + (that: Double): Double def + (that: Float): Float def + (that: Long): Long def + (that: Int): Int //analogicznie dla -, *, /, % def << (cnt: Int): Int //analogicznie dla >>, >>> def & (that: Long): Long def & (that: Int): Int //analogicznie dla, ^ def == (that: Double): Boolean def == (that: Float): Boolean def == (that: Long): Boolean //analogicznie dla!=, <, >, <=, >=

Hipotetyczna implementacja (liczby naturalne) abstract class Nat { def iszero: Boolean def predecessor: Nat def successor: Nat def +(that: Nat): Nat def -(that: Nat): Nat object Zero extends Nat { def iszero: Boolean = true def predecessor: Nat = error("negative number") def successor: Nat = new Succ(Zero) class Succ(x: Nat) extends Nat { def iszero: Boolean = false def predecessor: Nat = x def successor: Nat = new Succ(this) def +(that: Nat): Nat = x + that.successor def -(that: Nat): Nat = if (that.iszero) this else x - that.predecessor def +(that: Nat): Nat = that def -(that: Nat): Nat = if (that.iszero) Zero else error("neg. number") 33

34 Wyrażenia w sposób obiektowy Cel: zaimplementować wyliczanie wyrażeń postaci new Sum(new Number(1), new Sum(new Number(3), new Number(7))) Pierwsze podejście abstract class Expr { def isnumber: Boolean def issum: Boolean def numvalue: Int def leftop: Expr def rightop: Expr class Number(n: Int) extends Expr { def isnumber: Boolean = true def issum: Boolean = false def numvalue: Int = n def leftop: Expr = error("number.leftop") def rightop: Expr = error("number.rightop") class Sum(e1: Expr, e2: Expr) extends Expr { def isnumber: Boolean = false def issum: Boolean = true def numvalue: Int = error("sum.numvalue") def leftop: Expr = e1 def rightop: Expr = e2

35 Wyliczanie abstract class Expr { def isnumber: Boolean def issum: Boolean def numvalue: Int def leftop: Expr def rightop: Expr def eval(e: Expr): Int = { if (e.isnumber) e.numvalue else if (e.issum) eval(e.leftop) + eval(e.rightop) else error("unrecognized expression kind") Wady?

36 Podejście bardziej obiektowe Kod zebrany razem z danymi abstract class Expr { def eval: Int class Number(n: Int) extends Expr { def eval: Int = n class Sum(e1: Expr, e2: Expr) extends Expr { def eval: Int = e1.eval + e2.eval Łatwe dodawanie nowych klas class Prod(e1: Expr, e2: Expr) extends Expr { def eval: Int = e1.eval * e2.eval

37 Dodawanie nowej operacji zewnętrzna metoda def print(e: Expr) { if (e.isnumber) Console.print(e.numValue) else if (e.issum) { Console.print("(") print(e.leftop) Console.print("+") print(e.rightop) Console.print(")") else error("unrecognized...") modyfikacja całej hierarchii abstract class Expr { def eval: Int def print class Number(n: Int) extends Expr { def eval: Int = n def print { Console.print(n) class Sum(e1: Expr, e2: Expr) extends Expr { def eval: Int = e1.eval + e2.eval def print { Console.print("(") print(e1) Console.print("+") print(e2) Console.print(")")

38 Dodawanie nowej operacji Co więcej czasami nowa operacja ma grzebać w wielu klasach, np. upraszczanie moglibyśmy sobie radzić przy pomocy "brzydkich chwytów" x.isinstanceof[t] = x instanceof T x.asinstanceof[t] = (T) x

39 Case classes i case objects Definicja abstract class Expr case class Number(n: Int) extends Expr case class Sum(e1: Expr, e2: Expr) extends Expr Darmowe konstruktory def Number(n: Int) = new Number(n) def Sum(e1: Expr, e2: Expr) = new Sum(e1, e2) Sum(Sum(Number(1), Number(2)), Number(3)) Domyślna implementacja tostring, equals i hashcode Sum(Sum(Number(1), Number(2)), Number(3)) Sum(Number(1), Number(2)) == Sum(Number(1), Number(2)) Akcesory do wartości przekazanych w konstruktorze

40 Dopasowywanie wzorców Przykład def eval(e: Expr): Int = e match { case Number(x) => x case Sum(l, r) => eval(l) + eval(r) Uogólnienie switch na hierarchie klas (metoda każdego obiektu) Dopasowanie odbywa się na podstawie == Dopasowuje pierwszą klauzulę zmienne muszą się zaczynać małą literą np. n (odwołania do stałych dużą np. N, wyjątki true, false i null) każda zmienna może być użyta tylko raz, nie można Sum(x,x) wildcard _ (dopasowuje wartość, ale nie dowiązuje do nazwy) Jak nic się nie dopasuje to wyjątek MatchError

Przykład wyliczania eval(sum(number(1), Number(2))) -> (by rewriting the application) Sum(Number(1), Number(2)) match { case Number(n) => n case Sum(e1, e2) => eval(e1) + eval(e2) -> (by rewriting the pattern match) eval(number(1)) + eval(number(2)) -> (by rewriting the first application) Number(1) match { case Number(n) => n case Sum(e1, e2) => eval(e1) + eval(e2) + eval(number(2)) -> (by rewriting the pattern match) 1 + eval(number(2)) ->* 1 + 2 -> 3 41

42 Dopasowywanie wzorców jako metoda hierarchii Nie ma problemu abstract class Expr { def eval: Int = this match { case Number(n) => n case Sum(e1, e2) => e1.eval + e2.eval

43 Anonimowa funkcja wyrażenia case można używać bez funkcji match { case P1 => E1... case Pn => En jest to skrót dla funkcji anonimowej (x => x match { case P1 => E1... case Pn => En )

44 Obiekty zamiast importów statycznych object Sum { def apply(e1: Expr, e2: Expr) = new Sum(e1, e1) i teraz możemy pisać Sum(x,y) zamiast new Sum(x,y)

45 Dalszy lukier syntaktyczny Zamiast ((x,y)=>x+y)) można pisać (_ * _) kolejne _ reprezentują kolejne parametry funkcji