diff --git a/web/_config.yml b/web/_config.yml
index f534d6c2..bd16e9ac 100644
--- a/web/_config.yml
+++ b/web/_config.yml
@@ -5,86 +5,103 @@ lessons:
title_ko: 기초
title_ru: Основы
title_zh_cn: 基础
+ title_hr: Osnove
url: basics.html
desc: Values, functions, classes, methods, inheritance, try-catch-finally. Expression-oriented programming
desc_ko: 값, 함수, 클래스, 메소드, 상속, try-catch-finally. 식 중심의 프로그래밍
desc_ru: Переменные, функции, классы, методы, наследование, try-catch-finally. Программирование ориентированное на выражения
desc_zh_cn: 值,函数,类,方法,继承,try-catch-finally。面向表达式编程
+ desc_hr: Funkcije, klase, metode, nasljeđivanje, try-catch-finally. Expression-orijentirano programiranje
-
title: Basics continued
title_ko: 기초(계속)
title_ru: Основы языка. Продолжение
title_zh_cn: 基础知识(续)
+ title_hr: Nastavak osnova
url: basics2.html
desc: Case classes, objects, packages, apply, update, Functions are Objects (uniform access principle), pattern matching.
desc_ko: 케이스 클래스, 객체, 패키지, apply, update, 함수는 객체이다(동일한 억세스 원칙), 패턴 매치
desc_ru: Case классы, объекты, пакеты, apply, update, Функции как Объекты (единый принцип доступа), сопоставление с образцом.
desc_zh_cn: 样本类,对象,包,应用,更新,函数即对象(统一访问原则),模式匹配。
+ desc_hr: Case klase, objekti, paketi, update, Funkcije su Objekti (princip uniformnog pristupa), pattern match-iranje.
-
title: Collections
title_ko: 컬렉션
title_ru: Коллекции
title_zh_cn: 集合
+ title_hr: Kolekcije
url: collections.html
desc: Lists, Maps, functional combinators (map, foreach, filter, zip, folds)
desc_ko: 리스트, 맵, 함수 콤비네이터(map, foreach, filter, zip, fold들)
desc_ru: Списки, Карты, функциональные комбинаторы (map, foreach, filter, zip, folds)
desc_zh_cn: 列表,映射,功能组合(map, foreach, filter, zip, folds)
+ desc_hr: Liste, Mape, funkcionalni kombinatori (map, foreach, filter, zip, folds)
-
title: Pattern matching & functional composition
title_ko: 패턴 매치와 함수 합성
title_ru: Сопоставление с образцом и функкциональная композиция
title_zh_cn: 模式匹配与函数组合
+ title_hr: Pattern matchiranje & funkcionalna kompozicija
url: pattern-matching-and-functional-composition.html
desc: More functions! PartialFunctions, more Pattern Matching
desc_ko: 함수에 대한 추가 설명, 부분함수, 패턴 매치에 대한 설명
desc_ru: Еще больше функций! Частичные функции, еще больше сопоставления с образцом
desc_zh_cn: 更多函数!偏函数,更多模式匹配
+ desc_hr: Još funkcija! Partial Funkcije, još Pattern Match-iranja
-
title: Type & polymorphism basics
title_ko: 타입과 다형성의 기초
title_ru: Основы типов и полиморфизма
title_zh_cn: 类型和多态基础
+ title_hr: Tipovi & osnove polimorfizma
url: type-basics.html
desc: Basic Types and type polymorphism, type inference, variance, bounds, quantification
desc_ko: 기본 타입, 타입 다형성, 타입 추론, 공변성, 바운드, 한정하기
desc_ru: Основные типы и полиморфизм типов, вывод типов, изменчивость, пределы, квантификация
desc_zh_cn: 基本类型和类型多态性,类型推断,变性,边界,量化
+ desc_hr: Osnovni Tipovi i tipovi polimorfizma, nasljeđivanje tipova, variance, granice, kvantifikacija
-
title: Advanced types
title_ko: 타입(고급 주제)
title_ru: Дополнительные типы
title_zh_cn: 高级类型
+ title_hr: Napredni tipovi
url: advanced-types.html
desc: Advanced Types, view bounds, higher-kinded types, recursive types, structural types
desc_ko: 타입에 대한 추가 설명, 뷰 바운드, 상류 타입, 재귀적 타입, 구조적 타입
desc_ru: Дополнительные типы, видимое ограничение, типы высшего порядка, рекурсивные типы, структурные типы
desc_zh_cn: 高级类型,视界,更高级多态性类型,递归类型,结构类型
+ desc_hr: Napredni Tipovi, pregled granica, higher-kinded tipovi, rekurzivni tipovi, strukturalni tipovi
-
title: Simple Build Tool
title_ko: SBT
title_ru: Simple Build Tool
title_zh_cn: 简单构建工具
+ title_hr: Simple Build Tool
url: sbt.html
desc: All about SBT, the standard Scala build tool
desc_ko: 표준 스칼라 빌드 도구인 SBT에 대한 설명
desc_ru: Все о SBT, Стандартное средство сборки приложений для Scala
desc_zh_cn: 关于SBT——标准的Scala构建工具
+ desc_hr: Sve o SBT-u, standardni Scala build tool
-
title: More collections
title_ko: 컬렉션(계속)
title_ru: Подробнее о коллекциях
title_zh_cn: 更多的集合
+ title_hr: Još kolekcija
url: coll2.html
desc: Tour of the Scala Collections library
desc_ko: 스칼라 컬렉션 라이브러리 소개
desc_ru: Рассказ о библиотеке коллекций Scala
desc_zh_cn: Scala Collections库指南
+ desc_hr: Tura kroz Scala Collections biblioteku
-
title: Testing with specs
title_ko: specs로 테스트하기
title_ru: Тестирование с помощью specs
title_zh_cn: 使用specs测试
+ title_hr: Testiranje sa speckom
url: specs.html
desc_ko: Scala 테스트 프레임워크 Specs로 테스트하기
desc_ru: Написание тестов с помощью Specs, Scala BDD фреймфорк для тестирования
@@ -93,39 +110,47 @@ lessons:
title_ko: 스칼라의 동시성
title_ru: Параллельность в Scala
title_zh_cn: Scala 并发编程
+ title_hr: Konkurentnost u Scali
url: concurrency.html
desc: Runnable, Callable, threads, Futures
desc_ko: Runnable, Callable, 쓰레드, Future
desc_ru: Runnable, Callable, потоки, Futures
desc_zh_cn: Runnable, Callable, 线程, Futures
+ desc_hr: Runnable, Callable, dretve, Futures
-
title: Java + Scala
title_ko: Java + Scala
title_ru: Java + Scala
title_zh_cn: Java + Scala
+ title_hr: Java + Scala
url: java.html
desc: 'Java interop: Using Scala from Java'
desc_ko: '자바에서 스칼라 사용하기'
desc_ru: 'Java совместимость: Использование Scala из Java'
desc_zh_cn: 'Java跨平台交互:在Java中使用Scala'
+ desc_hr: 'Java interoperabilnost: Korištenje Scale iz Jave'
-
title: An introduction to Finagle
title_ko: 피네이글(Finagle) 소개
title_ru: Введение в Finagle
title_zh_cn: Finagle介绍
+ title_hr: Uvod u Finagle
url: finagle.html
desc: 'Finagle primitives: Future, Service, Filter, Builder'
desc_ko: '피네이글 기본 요소: Future, Servie, Filter, Builder'
desc_ru: 'Finagle примитивы: Future, Service, Filter, Builder'
desc_zh_cn: 'Finagle原语:Future, Service, Filter, Builder'
+ desc_hr: 'Finagle primitivi: Future, Service, Filter, Builder'
-
title: Searchbird
title_ko: 검색조(Searchbird)
title_ru: Searchbird
title_zh_cn: Searchbird
+ title_hr: Searchbird
url: searchbird.html
desc: Building a distributed search engine using Finagle
desc_ko: 피네이글로 분산 검색 엔진 만들기
desc_ru: Создание распределенного поискового движка, используя Finagle
desc_zh_cn: 利用Finagle构建一个分布式搜索引擎
+ desc_hr: Kreiranje distribuiranog algoritma pretraživanja koristeći Finagle
diff --git a/web/_layouts/default.html b/web/_layouts/default.html
index 88797dc4..3c0d8db2 100644
--- a/web/_layouts/default.html
+++ b/web/_layouts/default.html
@@ -55,7 +55,9 @@
Chinese simple translation by
jasonqu;
Korean translation by
- enshahar;
+ enshahar;
+ Croatian translation by
+ boljsa;
+scala> implicit def strToInt(x: String) = x.toInt +strToInt: (x: String)Int + +scala> "123" +res0: java.lang.String = 123 + +scala> val y: Int = "123" +y: Int = 123 + +scala> math.max("123", 111) +res1: Int = 123 ++ +Za tip granice koji zahtjeva da funkcija postoji za dani tip možete definirati pregled granice sa
<%
npr.
+
++scala> class Container[A <% Int] { def addIt(x: A) = 123 + x } +defined class Container ++ +Ovo govori da *A* mora biti vidljiv kao *Int*. Pa hajDm0 probati: + +
+scala> (new Container[String]).addIt("123") +res11: Int = 246 + +scala> (new Container[Int]).addIt(123) +res12: Int = 246 + +scala> (new Container[Float]).addIt(123.2F) ++ +h2(#otherbounds). Ostali tipovi granica + +Metode mogu primijeniti još složenije tipove granica kroz implicitne parametre. Npr.:8: error: could not find implicit value for evidence parameter of type (Float) => Int + (new Container[Float]).addIt(123.2) + ^ +
List
podržava sum
na numeričkom sadržaju ali ne i na drugom tipu sadržaja. Svi Scala numerički tipovi ne dijele nadklase pa zato ne možemo samo reći T <: Number
. Umjesto toga, Scala math biblioteka definira implicitni tip Numeric[T]
za prikladan T tip. Zatim ga List
definicija koristi:
+
++sum[B >: A](implicit num: Numeric[B]): B ++ +Ako pozovete
List(1,2).sum()
, ne morate proslijediti _num_ parameter; on je postavljen implicitno. Ali ako pozovete List("whoop").sum()
, iz nekog razloga se žali da ne može postaviti num
.
+
+Metode mogu pitati za neku vrstu specifičnog "dokaza" za tip bez postavljanja čudnih objekata kao Numeric
. Umjesto toga, možete koristiti jedan od ovih type-relation operatora:
+
+|A =:= B|A must be equal to B|
+|A <:< B|A must be a subtype of B|
+|A <%< B|A must be viewable as B|
+
+Ako dobijete pogreške pokušavajući koristiti <:< ili <%<, znajte da toga više nema u Scali 2.10. Bye, bye, nema više. Primjeri Scala Školice rade sa "Scalom 2.9.x":http://www.scala-lang.org/download/2.9.3.html Možete koristiti noviju verziju Scale, ali očekujte pogreške.
+
++scala> class Container[A](value: A) { def addIt(implicit evidence: A =:= Int) = 123 + value } +defined class Container + +scala> (new Container(123)).addIt +res11: Int = 246 + +scala> (new Container("123")).addIt ++ +Slično, gledajući naš prijašnji implicit, možemo napraviti sljedeće: + +:10: error: could not find implicit value for parameter evidence: =:=[java.lang.String,Int] +
+scala> class Container[A](value: A) { def addIt(implicit evidence: A <%< Int) = 123 + value } +defined class Container + +scala> (new Container("123")).addIt +res15: Int = 246 ++ +h3. Generičko programiranje sa view-ima + +In da Scala standard library, view-si se primarno koriste za implementaciju generičkih funkcija naspram kolekcija. +Npr. "min" funkcija (nad *Seq[]*), koristi ovu tehniku: + +
+def min[B >: A](implicit cmp: Ordering[B]): A = { + if (isEmpty) + throw new UnsupportedOperationException("empty.min") + + reduceLeft((x, y) => if (cmp.lteq(x, y)) x else y) +} ++ +Glavne prednosti ovoga su: + +* Item-si u kolekcijama nisu potrebni za implementaciju *Ordered*, ali *Ordered* se koristi kao statičko type check-iranje. +* Možete definirati svoje vlastito order-iranje bez dodatne podrške biblioteka: + +
+scala> List(1,2,3,4).min +res0: Int = 1 + +scala> List(1,2,3,4).min(new Ordering[Int] { def compare(a: Int, b: Int) = b compare a }) +res3: Int = 4 ++ +Kao napomena, postoje view-si u standardnom library-u koji prevode *Ordered* u *Ordering* (i obrnuto). + +
+trait LowPriorityOrderingImplicits { + implicit def ordered[A <: Ordered[A]]: Ordering[A] = new Ordering[A] { + def compare(x: A, y: A) = x.compare(y) + } +} ++ +h4. Kontekst granica & implicitly[] + +U Scali 2.8 uvedena je kratica za thread-ing pristupne implicitne argumente. + +
+scala> def foo[A](implicit x: Ordered[A]) {} +foo: [A](implicit x: Ordered[A])Unit + +scala> def foo[A : Ordered] {} +foo: [A](implicit evidence$1: Ordered[A])Unit ++ +Implicitnim vrijednostima se može pristupiti putem *implicitly* + +
+scala> implicitly[Ordering[Int]] +res37: Ordering[Int] = scala.math.Ordering$Int$@3a9291cf ++ +Kombinirano, ovo obično rezultira sa sažimanjem koda (less code), pogotovo kada se thread-a kroz view-se. + +h2(#higher). Higher-kinded tipovi & ad-hoc polimorfizam + +Scala može abstraktirati (ajme koji prijevod) iznad "higher kinded" tipova. Npr. pretpostavimo da morate koristiti nekoliko tipova kontenjera (ali ne onih za odlaganje otpada...) za više tipova podataka. Možete definirati
Container
interface koji može biti implementiran kao Option
, List
itd. Želite definirati interface za korištenje određenih vrijednosti u tim kontenjerima bez "zaključavanja" vrijednosnih (value) tipova.
+
+Ovo je analogno funkcijskom čišćenju (kao da se funkcija more analogno očistiti, prije digitalno...lala). Npr. gdje "unarni tip" ima konstruktore List[A]
, što znači da moramo zadovoljiti jednu "razinu" tipova varijabli kako bi kreirali konkretni tip (baš kao što "prljava" funkcija treba biti opskrbljena samo jednom listom argumenata kako bi bila pozvana), higher-kinded tip treba više, naravno, zato jer je higher-kinded pa je zahtjevan.
+
++scala> trait Container[M[_]] { def put[A](x: A): M[A]; def get[A](m: M[A]): A } + +scala> val container = new Container[List] { def put[A](x: A) = List(x); def get[A](m: List[A]) = m.head } +container: java.lang.Object with Container[List] = $anon$1@7c8e3f75 + +scala> container.put("hey") +res24: List[java.lang.String] = List(hey) + +scala> container.put(123) +res25: List[Int] = List(123) ++ +Napomena da je *Container* "polimorf" u parametriziranom tipu ("container type"). + +Ako kombiniramo kontenjere sa implicitima, dobijemo "ad-hoc" polimorfizam: mogućnost pisanja generičkih funkcija preko kontenjera. + +
+scala> trait Container[M[_]] { def put[A](x: A): M[A]; def get[A](m: M[A]): A } + +scala> implicit val listContainer = new Container[List] { def put[A](x: A) = List(x); def get[A](m: List[A]) = m.head } + +scala> implicit val optionContainer = new Container[Some] { def put[A](x: A) = Some(x); def get[A](m: Some[A]) = m.get } + +scala> def tupleize[M[_]: Container, A, B](fst: M[A], snd: M[B]) = { + | val c = implicitly[Container[M]] + | c.put(c.get(fst), c.get(snd)) + | } +tupleize: [M[_],A,B](fst: M[A],snd: M[B])(implicit evidence$1: Container[M])M[(A, B)] + +scala> tupleize(Some(1), Some(2)) +res33: Some[(Int, Int)] = Some((1,2)) + +scala> tupleize(List(1), List(2)) +res34: List[(Int, Int)] = List((1,2)) ++ +h2(#fbounded). F-bounded polimorfizam + +Često je potrebno pristupiti određenoj podklasi u (generičkom) trait-u. Npr. zamislite da imate neki trait koji je generički, ali može biti uspoređivan sa određenom podklasom tog trait-a. + +
+trait Container extends Ordered[Container] ++ +To zahtjeva od podklase da implementira compare metodu + +
+def compare(that: Container): Int ++ +I zato ne možemo pristupiti konkretnom podtipu, npr.: + +
+class MyContainer extends Container { + def compare(that: MyContainer): Int +} ++ +jer se ne može kompajlirati, budući da smo definirali Ordered za *Container*, a ne određeni podtip. + +Alternativno rješenje bi bilo parametrizirati Container tako da možemo pristupiti podtipu u podklasi. + +
+trait Container[A] extends Ordered[A] ++ +Podklasa sada može: + +
+class MyContainer extends Container[MyContainer] { + def compare(that: MyContainer): Int +} ++ +Ali problem je da tip A nije ograničen ničime i potencijalno možete napraviti: + +
+class MyContainer extends Container[String] { + def compare(that: String): Int +} ++ +Kako bi to izbjegli koristimo F-bounded polimorfizam. + +
+trait Container[A <: Container[A]] extends Ordered[A] ++ +Čudan tip! Ali primijetite da je Ordered parametriziran na *A*, koji je sam *Container[A]* + +Sada: + +
+class MyContainer extends Container[MyContainer] { + def compare(that: MyContainer) = 0 +} ++ +Sada su posloženi: + +
+scala> List(new MyContainer, new MyContainer, new MyContainer) +res3: List[MyContainer] = List(MyContainer@30f02a6d, MyContainer@67717334, MyContainer@49428ffa) + +scala> List(new MyContainer, new MyContainer, new MyContainer).min +res4: MyContainer = MyContainer@33dfeb30 ++ +Budući da su svi podtipovi *Container[_]*, možemo definirati drugu podklasu & kreirati miješanu (ali ne pizzu) listu *Container[_]*: + +
+scala> class YourContainer extends Container[YourContainer] { def compare(that: YourContainer) = 0 } +defined class YourContainer + +scala> List(new MyContainer, new MyContainer, new MyContainer, new YourContainer) +res2: List[Container[_ >: YourContainer with MyContainer <: Container[_ >: YourContainer with MyContainer <: ScalaObject]]] + = List(MyContainer@3be5d207, MyContainer@6d3fe849, MyContainer@7eab48a7, YourContainer@1f2f0ce9) ++ +Primijetite kako je tip rezultata sada ograničen sa *YourContainer with MyContainer*. Ovo je rad type inferencer-a. Zanimljivo- ovaj tip ne mora imati smisla, on samo pruža logički viši-niži graničnik (jeste se malo izgubili, viši, niži, dajte se odlučite) za unificirani tip liste. Što se događa ako pokušamo koristiti *Ordered*? + +
+(new MyContainer, new MyContainer, new MyContainer, new YourContainer).min ++ +Ne postoji *Ordered[]* za unificirani tip. Šteta. + +h2(#structural). Strukturalni tipovi + +Scala ima podršku za strukturalne tipove* -- zahtjevi tipa su izraženi sa interface _structure_ naspram konkretnog tipa. + +:9: error: could not find implicit value for parameter cmp: + Ordering[Container[_ >: YourContainer with MyContainer <: Container[_ >: YourContainer with MyContainer <: ScalaObject]]] +
+scala> def foo(x: { def get: Int }) = 123 + x.get +foo: (x: AnyRef{def get: Int})Int + +scala> foo(new { def get = 10 }) +res0: Int = 133 ++ +Ovo može biti korisno u mnogim situacijama, ali implementacija koristi refleksiju, zato budite performance-aware! + +h2(#abstractmem). Abstraktni tipovi članova + +U trait-u, možete ostaviti type članove abstraktnima. + +
+scala> trait Foo { type A; val x: A; def getX: A = x } +defined trait Foo + +scala> (new Foo { type A = Int; val x = 123 }).getX +res3: Int = 123 + +scala> (new Foo { type A = String; val x = "hey" }).getX +res4: java.lang.String = hey ++ +To je koristan trik kada radite dependency injection itd. + +Možete se referencirati prema abstraktnom tipu varijable koristeći hash operator: + +
+scala> trait Foo[M[_]] { type t[A] = M[A] } +defined trait Foo + +scala> val x: Foo[List]#t[Int] = List(1) +x: List[Int] = List(1) ++ + +h2(#manifest). Tipovi brisanja & manifesti + +Koliko je nama poznato, tip informacije je izgubljen na compile time-u zbog _erasure_. Scala ima *Manifests*, što nam dopušta selektivno vraćanje tipa informacija. Manifesti su implicitne vrijednosti, generirani od strane kompajlera prema potrebi. + +
+scala> class MakeFoo[A](implicit manifest: Manifest[A]) { def make: A = manifest.erasure.newInstance.asInstanceOf[A] } + +scala> (new MakeFoo[String]).make +res10: String = "" ++ +h2(#finagle). Case studija: Finagle + +Pogledajte: "https://github.com/twitter/finagle":https://github.com/twitter/finagle + +
+trait Service[-Req, +Rep] extends (Req => Future[Rep]) + +trait Filter[-ReqIn, +RepOut, +ReqOut, -RepIn] + extends ((ReqIn, Service[ReqOut, RepIn]) => Future[RepOut]) +{ + def andThen[Req2, Rep2](next: Filter[ReqOut, RepIn, Req2, Rep2]) = + new Filter[ReqIn, RepOut, Req2, Rep2] { + def apply(request: ReqIn, service: Service[Req2, Rep2]) = { + Filter.this.apply(request, new Service[ReqOut, RepIn] { + def apply(request: ReqOut): Future[RepIn] = next(request, service) + override def release() = service.release() + override def isAvailable = service.isAvailable + }) + } + } + + def andThen(service: Service[ReqOut, RepIn]) = new Service[ReqIn, RepOut] { + private[this] val refcounted = new RefcountedService(service) + + def apply(request: ReqIn) = Filter.this.apply(request, refcounted) + override def release() = refcounted.release() + override def isAvailable = refcounted.isAvailable + } +} ++ +Servis može autorizirati zahtjeve putem filtera. + +
+trait RequestWithCredentials extends Request { + def credentials: Credentials +} + +class CredentialsFilter(credentialsParser: CredentialsParser) + extends Filter[Request, Response, RequestWithCredentials, Response] +{ + def apply(request: Request, service: Service[RequestWithCredentials, Response]): Future[Response] = { + val requestWithCredentials = new RequestWrapper with RequestWithCredentials { + val underlying = request + val credentials = credentialsParser(request) getOrElse NullCredentials + } + + service(requestWithCredentials) + } +} ++ +Primijetite kako pozadinski servis zahtjeva autorizirani zahtjev i da je to statički verificirano. Filteri stoga mogu biti smatrani kao servis transformeri. Transformer... + +Mnogi filteri se mogu koristiti zajedno: + +
+val upFilter = + logTransaction andThen + handleExceptions andThen + extractCredentials andThen + homeUser andThen + authenticate andThen + route ++ +Tipkajte sigurno (Type safely)! diff --git a/web/hr/basics.textile b/web/hr/basics.textile new file mode 100644 index 00000000..c143f216 --- /dev/null +++ b/web/hr/basics.textile @@ -0,0 +1,437 @@ +--- +layout: post +title: Osnove +next: basics2.textile +--- + +Ova lekcija sadržava: +* "O ovoj lekciji":#overview +* "expressions":#expressions +* "values":#val +* "funkcije":#functions +* "klase":#class +* "osnovno nasljeđivanje":#extends +* "trait-ovi":#trait +* "tipovi":#types + +h2(#overview). O ovoj lekciji + +U nekoliko prvih tjedana proći ćemo kroz osnovne koncepte i sintaksu, zatim ćemo proširivati znanje koristeći primjere. + +Neki primjeri će biti u obliku izvornog koda, dok će drugi biti pisani u obliku interpretera, za svakog ponešto, lalal + +Preporučamo da imate dostupan interpreter, to će vam olakšati baratanje primjerima, yeah. + +h3. Zašto Scala? + +* Ekspresivan +** Funkcije prvog reda (first-class functions) +** Closures +* Sažet +** Type inference +** Dosljedna sintaksa za kreiranje funkcija +* Java interoperabilinost +** Mogućnost iskorištavanja Java biblioteka +** Mogućnost iskorištavanja Java alata +** Bez penala što se tiče performansi + +h3. Kako sa Scala-om? + +* Kompajlira se u Java bytecode +* Radi sa standardnim JVM-om +** I čak sa nekim nestandardnim JVM-ima kao što je Dalvik +** Scala kompajler je napisao lik koji je autor Java kompajlera, idesh + +h3. Misli kao Scalaš + +Scala nije samo ljepša Java. Trebate ju učiti otvorenog uma, tak je bolje, vjerujte nam na riječ, časna pionirska. + +h3. Daj mi Scalu + +Primjeri Scala školice rade sa "Scala 2.9.x":http://www.scala-lang.org/download/2.9.3.html +Ako koristite Scalu 2.10.x ili noviju, _većina_ primjera će raditi ali ne svi, sori, takav je život. + +h3. Pokrenimo Interpreter + +Pokrenite
sbt console
.
+
++$ sbt console + +[...] + +Welcome to Scala version 2.8.0.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_20). +Type in expressions to have them evaluated. +Type :help for more information. + +scala> ++ + +h2(#expressions). Expressions + +
+scala> 1 + 1 +res0: Int = 2 ++ +res0 je automatski kreirana vrijednost koju je izdao interpreter kao rezultat vašeg izraza. res0 je tipa Int i sadrži Integer 2. + +Gotovo sve u Scali je izraz. + +h2(#val). Values + +Rezultatu vašeg izraza možete dati ime, idesh, cool. + +
+scala> val two = 1 + 1 +two: Int = 2 ++ +h3. Varijable + +Var nam služi za, hm, varijabilne vrijednosti, tko bi rekao,
var
.
+
++scala> var name = "steve" +name: java.lang.String = steve + +scala> name = "marius" +name: java.lang.String = marius ++ +h2(#functions). Funkcije + +Možete kreirati funkcije pomoću def. + +
+scala> def addOne(m: Int): Int = m + 1 +addOne: (m: Int)Int ++ +U Scali, morate definirati tip potpisa za funkcijske parametre. Interpreter vam vraća type signature dok si rekao keks, keks, keks. + +
+scala> val three = addOne(2) +three: Int = 3 ++ +Funkcije možete ostaviti bez argumenata, neće se ljutiti. + +
+scala> def three() = 1 + 2 +three: ()Int + +scala> three() +res2: Int = 3 + +scala> three +res3: Int = 3 ++ +h3. Anonimne funkcije + +Možete kreirati anonimne funkcije. + +
+scala> (x: Int) => x + 1 +res2: (Int) => Int =+ +Ova funkcija dodaje 1 Int-u naziva x. + ++
+scala> res2(1) +res3: Int = 2 ++ +Možete proslijeđivati anonimne funkcije ili ih spremiti kao vrijednosti u val. + +
+scala> val addOne = (x: Int) => x + 1 +addOne: (Int) => Int =+ +Ako se vaša funkcija sastoji od mnoštva izraza, možete koristiti {} + ++ +scala> addOne(1) +res4: Int = 2 +
+def timesTwo(i: Int): Int = { + println("hello world") + i * 2 +} ++ +Isto vrijedi i za anonimne funkcije. + +
+scala> { i: Int => + println("hello world") + i * 2 +} +res0: (Int) => Int =+ +Primijetiti ćete da se ova sintaksa koristi kada se anonimna funkcija prenosi kao argument. + +h3. Partial application + +Funkciju možete definirati sa _ znakom, što vam daje drugu funkciju. Scala koristi _ znak sa raznim značenjima i u raznim kontekstima, ali _ možete zamisliti kao neimenovani magični wildcard. U kontekstu+
{ _ + 2 }
znači: neimenovani parametar. Možete ga koristiti kao:
+
++scala> def adder(m: Int, n: Int) = m + n +adder: (m: Int,n: Int)Int ++ +
+scala> val add2 = adder(2, _:Int) +add2: (Int) => Int =+ +Možete djelomično pridružiti bilo koji argument u listi argumenata, ne samo jedan. + +h3. Curried funkcije + +Ponekad ima smisla dopustiti ljudima pristup pridruživanju nekih argumenata vašim funkcijama sada i ostalim likovima dopustiti to isto nešto kasnije. + +Ovdje je primjer funkcije koja vam dopušta napraviti množenje dva broja (back to the elementary school, lalal). +U jednom pozivu odlučujete koji lik je množitelj (tužitelj) a koji množenik (tuženik). + ++ +scala> add2(3) +res50: Int = 5 +
+scala> def multiply(m: Int)(n: Int): Int = m * n +multiply: (m: Int)(n: Int)Int ++ +Možete pozvati direktno sa oba parametra. + +
+scala> multiply(2)(3) +res0: Int = 6 ++ +Možete popuniti prvi parametar i djelomično pridružiti drugi. + +
+scala> val timesTwo = multiply(2) _ +timesTwo: (Int) => Int =+ +Možete uzeti bilo koju funkciju sa više argumenata i curry-ati ga. Pokušajmo sa+ +scala> timesTwo(3) +res1: Int = 6 +
adder
+
++scala> val curriedAdd = (adder _).curried +curriedAdd: Int => (Int => Int) =+ +h3. Varijabilna duljina argumenata + +Postoji posebna sintaksa za metode koje mogu primati parametre ponavljajućeg tipa. Kako bi primijenili String+ +scala> val addTwo = curriedAdd(2) +addTwo: Int => Int = + +scala> addTwo(4) +res22: Int = 6 +
capitalize
funkciju na više stringova, možete napisati:
+
++def capitalizeAll(args: String*) = { + args.map { arg => + arg.capitalize + } +} + +scala> capitalizeAll("rarity", "applejack") +res2: Seq[String] = ArrayBuffer(Rarity, Applejack) ++ +h2(#class). Klase + +
+scala> class Calculator { + | val brand: String = "HP" + | def add(m: Int, n: Int): Int = m + n + | } +defined class Calculator + +scala> val calc = new Calculator +calc: Calculator = Calculator@e75a11 + +scala> calc.add(1, 2) +res1: Int = 3 + +scala> calc.brand +res2: String = "HP" ++ +Prikazani su primjeri u kojima se definiraju metode sa def i polja sa val. Metode su samo funkcije koje mogu pristupati stanjima klase. + +h3. Konstruktor + +Konstruktori nisu posebne metode, oni su kod koji je van definicije metoda u vašoj klasi. Proširimo naš Calculator primjer kako bi uzeli konstruktor argument i zatim ga iskoristili kako bi inicijalizirali interno stanje. + +
+class Calculator(brand: String) { + /** + * A constructor. + */ + val color: String = if (brand == "TI") { + "blue" + } else if (brand == "HP") { + "black" + } else { + "white" + } + + // An instance method. + def add(m: Int, n: Int): Int = m + n +} ++ +Primijetite dva različita stila komentara. + +Možete koristiti konstruktor kako bi konstruirali instancu: + +
+scala> val calc = new Calculator("HP") +calc: Calculator = Calculator@1e64cc4d + +scala> calc.color +res0: String = black ++ +h3. Expressions + +Naš Calculator primjer prikazuje kako je Scala expression-orijentirana. Boja vrijednosti (value color) je ograničena ovisno o if/else izrazu. Scala je expression-orijentirana: većina stvari su expression-i više nego statement-i. + +h3. Funkcije će se potući sa Metodama iliti Functions vs Methods + +Funkcije i metode su većinom razmjenjive (interchangeable). Zato jer su funkcije i metode toliko slične, možda niste zapamtili da pozivate thing bez obzira radi li se o funkciji ili metodi. Kada uskočite u dubinu različitosti metoda i funkcija, možda ostanete paf tj. možda vas te razlike zbune, uh. + +
+scala> class C { + | var acc = 0 + | def minc = { acc += 1 } + | val finc = { () => acc += 1 } + | } +defined class C + +scala> val c = new C +c: C = C@1af1bd6 + +scala> c.minc // calls c.minc() + +scala> c.finc // returns the function as a value: +res2: () => Unit =+ +Kada zovete jednu "funkciju" bez zagrada ali drugu sa zagradama, možete pomisliti Uppsss, mislio sam da sam znao kako Scala funkcije rade, ali očito šipak, niš od toga. Možda onda ponekad zatrebate zagrade? Možda razumijete funkcije, ali koristite metodu. + +U praksi, možete raditi sjajne stvari u Scali ostajući u magli promatrajući razlike između metoda i funkcija. Ako ste novi u Scali i čitate explanations of the differences, možda ćete imati problema pratiti razlike. To ne znači da ćete imati problema u korištenju Scale. To samo znači da je razlika između funkcija i metoda jako, jako, prepredena iliti lukava. + +h2(#extends). Nasljeđivanje + ++
+class ScientificCalculator(brand: String) extends Calculator(brand) { + def log(m: Double, base: Double) = math.log(m) / math.log(base) +} ++ +*Dobro je pogledati* Efektivna Scala pokazuje da je Type alias bolji od
extends
kada podklasa nije zapravo različita od nadklase. Tura kroz Scalu opisuje Subclassing.
+
+h3. Overloading metode
+
++class EvenMoreScientificCalculator(brand: String) extends ScientificCalculator(brand) { + def log(m: Int): Double = log(m, math.exp(1)) +} ++ +h3. Abstraktne klase + +Možete definirati abstract class, klasu koja definira neke metode ali ih ne implementira. Podklase extend-aju abstraktne klase i definiraju te metode. Ne možete kreirati instancu abstraktne klase. + +
+scala> abstract class Shape { + | def getArea():Int // subclass should define this + | } +defined class Shape + +scala> class Circle(r: Int) extends Shape { + | def getArea():Int = { r * r * 3 } + | } +defined class Circle + +scala> val s = new Shape ++ +h2(#trait). Traits + +:8: error: class Shape is abstract; cannot be instantiated + val s = new Shape + ^ + +scala> val c = new Circle(2) +c: Circle = Circle@65c0035b +
traits
su kolekcije polja i ponašanja koja možete extend-ati ili miješati sa vašim klasama.
+
++trait Car { + val brand: String +} + +trait Shiny { + val shineRefraction: Int +} ++ +
+class BMW extends Car { + val brand = "BMW" +} ++ +Jedna klasa može extend-ati više trait-ova koristeći
with
ključnu riječ:
+
++class BMW extends Car with Shiny { + val brand = "BMW" + val shineRefraction = 12 +} ++ +*Dobro je pogledati* Efektivna Scala ima mišljenja o trait-ovima. + +*Kada želite Trait umjesto abstraktne klase?* Ako želite definirati interface-like tip, možda ćete teže izabrati između trait-a i abstraktne klase. Oba vam pružaju mogućnost definiranja tipa sa određenim ponašanjem, pitajući extender-e da definiraju neko drugo ponašanje. Kratke napomene: + +
trait t(i: Int) {}
; i
parametar je ilegalan.
+Int
koji je podtip Number tipa. Funkcije također mogu biti generičke i bilo kojeg tipa. Kada se to dogodi, vidjeti ćete type parametar sa square bracket sintaksom. Slijedi primjer:
+
++trait Cache[K, V] { + def get(key: K): V + def put(key: K, value: V) + def delete(key: K) +} ++ +Metode također mogu imati type parametre. + +
+def remove[K](key: K) +diff --git a/web/hr/basics2.textile b/web/hr/basics2.textile new file mode 100644 index 00000000..6661f693 --- /dev/null +++ b/web/hr/basics2.textile @@ -0,0 +1,330 @@ +--- +prev: basics.textile +next: collections.textile +title: Nastavak osnova +layout: post +--- + +Ova lekcija sadržava: +* "apply":#apply +* "objekti":#object +* "Funkcije su Objekti":#fnobj +* "paketi":#package +* "pattern matching":#match +* "case klase":#caseclass +* "try-catch-finally":#exception + +h2(#apply). apply metode + +apply metode nam pružaju lijepu sintaksu (syntactic sugar) kada klasa ili objekt ima jednu glavnu svrhu. + +
+scala> class Foo {} +defined class Foo + +scala> object FooMaker { + | def apply() = new Foo + | } +defined module FooMaker + +scala> val newFoo = FooMaker() +newFoo: Foo = Foo@5b83f762 ++ +ili + +
+scala> class Bar { + | def apply() = 0 + | } +defined class Bar + +scala> val bar = new Bar +bar: Bar = Bar@47711479 + +scala> bar() +res8: Int = 0 ++ +Ovdje naša instanca objekta izgleda kao da pozivamo metodu. Više o tome kasnije! + +h2(#object). Objekti + +Objekti se koriste kako bi sadržavali jednu instancu klase. + +
+object Timer { + var count = 0 + + def currentCount(): Long = { + count += 1 + count + } +} ++ +Kako koristiti + +
+scala> Timer.currentCount() +res0: Long = 1 ++ +Klase i Objekti mogu imati jednako ime. Objekt se zove 'Companion Object'. + +Slijedi jednostavan primjer kojemu je jedina svrha ukloniti potrebu korištenja 'new' kako bi kreirali instancu. + +
+class Bar(foo: String) + +object Bar { + def apply(foo: String) = new Bar(foo) +} ++ + +h2(#fnobj). Funkcije su Objekti + +U Scali, često pričamo o objektno-funkcionalnom programiranju. Što to znači? Što je zapravo Funkcija? + +Funkcija je set trait-ova. Točnije, funkcija koja uzima jedan argument je instanca Function1 trait-a. Ovaj trait definira
apply()
sintatik šugar koji smo spominjali ranije, dopuštajući vam da pozovete objekt kao što bi zvali funkciju.
+
++scala> object addOne extends Function1[Int, Int] { + | def apply(m: Int): Int = m + 1 + | } +defined module addOne + +scala> addOne(1) +res2: Int = 2 ++ +Postoji Function0 - 22. Zašto 22? To je magični broj. Nikad nisam trebao funkciju sa više od 22 argumenta. + +Sintatički šugar apply-a pomaže ujediniti dualnost objektnog i funkcionalnog programiranja. Možete proslijeđivati klase i koristiti ih kao funkcije jer su funkcije samo instance klasa. + +Znači li to da svaki put kada definirate metodu u vašoj klasi, zapravo dobivate instancu Function*? Ne, metode u klasama su metode. Metode su definirane samostalno. + +Klase također mogu extend-ati Function i takve instance se mogu pozvati sa (). + +
+scala> class AddOne extends Function1[Int, Int] { + | def apply(m: Int): Int = m + 1 + | } +defined class AddOne + +scala> val plusOne = new AddOne() +plusOne: AddOne =+ +Lijepi prečac za+ +scala> plusOne(1) +res0: Int = 2 +
extends Function1[Int, Int]
je extends (Int => Int)
+
++class AddOne extends (Int => Int) { + def apply(m: Int): Int = m + 1 +} ++ +h2(#package). Paketi + +Možete organizirati vaš kod unutar paketa + +
+package com.twitter.example ++ +na početku datoteke. + +Vrijednosti i funkcije ne mogu biti van klase ili objekta. Objekti su koristan alat za organizaciju statičkih funkcija. + +
+package com.twitter.example + +object colorHolder { + val BLUE = "Blue" + val RED = "Red" +} ++ +Sada možete pristupiti članovima direktno + +
+println("the color is: " + com.twitter.example.colorHolder.BLUE) ++ +Primijetite što Scala repl kaže kada definirate objekt: + +
+scala> object colorHolder { + | val Blue = "Blue" + | val Red = "Red" + | } +defined module colorHolder ++ +Ovo vam daje mali nagovještaj da su dizajneri Scale kreirali objekte kako bi bili dio Scala modul sustava. + +h2(#match). Pattern Matching + +Jedan od najkorisnijih dijelova Scale. + +Match-iranje na vrijednostima + +
+val times = 1 + +times match { + case 1 => "one" + case 2 => "two" + case _ => "some other number" +} ++ +Match-iranje sa guard-ovima + +
+times match { + case i if i == 1 => "one" + case i if i == 2 => "two" + case _ => "some other number" +} ++ +Primijetite kako smo uhvatili vrijednost u varijabli 'i'. + +
_
u zadnjem case izrazu je wildcard; on osigurava da možemo obraditi svaki zahtjev. Inače ćete dobiti runtime error (a to ne želimo, no, no) ako proslijedite broj koji se ne match-ira. Više o tome kasnije.
+
+*Dobro je pogledati* Efektivna Scala ima mišljenje o when to use pattern matching i pattern matching formatting. Tura kroz Scalu opisuje Pattern Matching
+
+h3. Match-iranje na tipovima
+
+Možete koristiti match
kako bi obradili vrijednosti različitih tipova na različit način.
+
++def bigger(o: Any): Any = { + o match { + case i: Int if i < 0 => i - 1 + case i: Int => i + 1 + case d: Double if d < 0.0 => d - 0.1 + case d: Double => d + 0.1 + case text: String => text + "s" + } +} ++ +h3. Match-iranje na članovima klase + +Prisjetimo se našeg Calculator-a iz prijašnjih primjera. + +Idemo ga klasificirati prema tipu. + +Prvo teži način. + +
+def calcType(calc: Calculator) = calc match { + case _ if calc.brand == "HP" && calc.model == "20B" => "financial" + case _ if calc.brand == "HP" && calc.model == "48G" => "scientific" + case _ if calc.brand == "HP" && calc.model == "30B" => "business" + case _ => "unknown" +} ++ +Idesh, ovo je boljelo. Dobro da Scala pruža lipe alate za ovakve stvari, juhuu. + +h2(#caseclass). Case Klase + +case klase se koriste kako bi prikladno pohranili i match-irali nad sadržajem klase. Možete ih kreirati bez korištenja new. + +
+scala> case class Calculator(brand: String, model: String) +defined class Calculator + +scala> val hp20b = Calculator("HP", "20b") +hp20b: Calculator = Calculator(hp,20b) + ++ +case klase automatski imaju jednakost i lijepu toString metodu baziranu na argumentima konstruktora. + +
+scala> val hp20b = Calculator("HP", "20b") +hp20b: Calculator = Calculator(hp,20b) + +scala> val hp20B = Calculator("HP", "20b") +hp20B: Calculator = Calculator(hp,20b) + +scala> hp20b == hp20B +res6: Boolean = true ++ +case klase mogu imati metode baš kao normalne klase. + +h6. Case Klase sa pattern match-ingom + +case klase su dizajnirane kako bi se koristile sa pattern match-ingom. Pojednostavimo naš calculator klasifikator primjer. + +
+val hp20b = Calculator("HP", "20B") +val hp30b = Calculator("HP", "30B") + +def calcType(calc: Calculator) = calc match { + case Calculator("HP", "20B") => "financial" + case Calculator("HP", "48G") => "scientific" + case Calculator("HP", "30B") => "business" + case Calculator(ourBrand, ourModel) => "Calculator: %s %s is of unknown type".format(ourBrand, ourModel) +} ++ +Druge alternative za zadnji match + +
+ case Calculator(_, _) => "Calculator of unknown type" ++ + ILI jednostavno možemo ne specificirati da je to Calculator. + +
+ case _ => "Calculator of unknown type" ++ + ILI možemo napraviti re-bind match-irane vrijednosti sa drugim imenom + +
+ case c@Calculator(_, _) => "Calculator: %s of unknown type".format(c) ++ +h2(#exception). Iznimke + +Iznimke su dostupne u Scali kroz try-catch-finally sintaksu koja koristi pattern matching. + +
+try { + remoteCalculatorService.add(1, 2) +} catch { + case e: ServerIsDownException => log.error(e, "the remote calculator service is unavailable. should have kept your trusty HP.") +} finally { + remoteCalculatorService.close() +} ++ +
try
-evi su također expression-orijentirani
+
++val result: Int = try { + remoteCalculatorService.add(1, 2) +} catch { + case e: ServerIsDownException => { + log.error(e, "the remote calculator service is unavailable. should have kept your trusty HP.") + 0 + } +} finally { + remoteCalculatorService.close() +} ++ +Ovo nije primjer odličnog programerskog stila, samo primjer try-catch-finally koji rezultira u expressions-ima kao i većina stvari u Scali. + +Finally će se zvati nakon što je iznimka obrađena i nije dio expression-a. diff --git a/web/hr/coll2.textile b/web/hr/coll2.textile new file mode 100644 index 00000000..a6b69bd0 --- /dev/null +++ b/web/hr/coll2.textile @@ -0,0 +1,395 @@ +--- +prev: sbt.textile +next: specs.textile +title: Još kolekcija +layout: post +--- + +Scala pruža lijep set implementacija kolekcija. Također pruža neke apstrakcije nad tipovima kolekcija. To vam dopušta pisanje koda koji može raditi sa kolekcijama tipa
Foo
bez brige je li kolekcija List
, Set
, ili što već...
+
+"Ova stranica":http://www.decodified.com/scala/collections-api.xml pruža dobar način kako implementirati na standardan način i sve povezati sa scaladoc-om.
+
+* "Osnove":#basics Tipovi kolekcija koje ćete stalno koristiti
+* "Hijerahija":#hierarchy Apstrakcije kolekcija
+* "Metode":#methods
+* "Mutable":#mutable
+* "Java kolekcije":#java jednostavno funkcioniraju
+
+h2(#basics). Osnove
+
+h3. List
+
+Standardna link-ana lista.
+
++scala> List(1, 2, 3) +res0: List[Int] = List(1, 2, 3) ++ +Možete s njom sve što bi i očekivali u funkcijskom jeziku. + +
+scala> 1 :: 2 :: 3 :: Nil +res1: List[Int] = List(1, 2, 3) ++ +*Dobro je pogledati* "API doc":http://www.scala-lang.org/api/current/scala/collection/immutable/List.html + +h3. Set + +Set-ovi nemaju duplikate + +
+scala> Set(1, 1, 2) +res2: scala.collection.immutable.Set[Int] = Set(1, 2) ++ +*Dobro je pogledati* "API doc":http://www.scala-lang.org/api/current/scala/collection/immutable/Set.html + +h3. Seq + +Sekvence imaju definiran redoslijed. + +
+scala> Seq(1, 1, 2) +res3: Seq[Int] = List(1, 1, 2) ++ +(Primijetite da je to vratilo listu. Seq je trait; List je implementacija Seq-a. Postoji standardni objekt
Seq
koji, kao što ovdje vidite, kreira Lists.)
+
+*Dobro je pogledati* "API doc":http://www.scala-lang.org/api/current/scala/collection/Seq.html
+
+h3. Map
+
+Mape su key value kontenjeri.
+
++scala> Map('a' -> 1, 'b' -> 2) +res4: scala.collection.immutable.Map[Char,Int] = Map((a,1), (b,2)) ++ +*Dobro je pogledati* "API doc":http://www.scala-lang.org/api/current/scala/collection/immutable/Map.html + +h2(#hierarchy). Hijerarhija + +To su sve trait-ovi, mutable i immutable paketi imaju standardne i posebne implementacije. + +h3. Traversable + +Sve kolekcije mogu biti međusobno miješane. Ovaj trait definira standardne funkcijske kombinatore. Ovi kombinatori su pisani u stilu @foreach@, koje kolekcije moraju implementirati. + +*Dobro je pogledati* "API doc":http://www.scala-lang.org/api/current/scala/collection/Traversable.html + +h3. Iterable + +@iterator()@ metoda nam pruža Iterator kroz elemente. + +*Dobro je pogledati* "API doc":http://www.scala-lang.org/api/current/scala/collection/Iterable.html + +h3. Seq + +Sekvenca item-a sa redoslijedom (ordering). + +*Dobro je pogledati* "API doc":http://www.scala-lang.org/api/current/scala/collection/Seq.html + +h3. Set + +Kolekcija item-a bez duplikata. + +*Dobro je pogledati* "API doc":http://www.scala-lang.org/api/current/scala/collection/immutable/Set.html + +h3. Map + +Key Value Pairs. + +*Dobro je pogledati* "API doc":http://www.scala-lang.org/api/current/scala/collection/immutable/Map.html + +h2(#methods). Metode + +h3. Traversable + +Sve ove metode su odmah dostupne. Argumenti i povratni tipovi neće uvijek izgledati isto budući da ih podklase mogu pregaziti (override). + +
+def head : A +def tail : Traversable[A] ++ +Ovdje su Funkcionalni Kombinatori definirani. + +
+def map [B] (f: (A) => B) : CC[B]
+
+
+vraća kolekciju sa svakim elementom transformiranim sa @f@
+
+
+def foreach[U](f: Elem => U): Unit
+
+
+izvršava @f@ kroz svaki element u kolekciji.
+
+
+def find (p: (A) => Boolean) : Option[A]
+
+
+vraća prvi element koji zadovoljava funkciju
+
+
+def filter (p: (A) => Boolean) : Traversable[A]
+
+
+vraća kolekciju sa svim elementima koji zadovoljavaju funkciju
+
+Particioniranje:
+
+
+def partition (p: (A) ⇒ Boolean) : (Traversable[A], Traversable[A])
+
+
+Dijeli kolekciju u dva dijela prema funkciji
+
+
+def groupBy [K] (f: (A) => K) : Map[K, Traversable[A]]
+
+
+Konverzija:
+
+Zanimljivo, možete konvertirati jednu kolekciju u drugu.
+
++def toArray : Array[A] +def toArray [B >: A] (implicit arg0: ClassManifest[B]) : Array[B] +def toBuffer [B >: A] : Buffer[B] +def toIndexedSeq [B >: A] : IndexedSeq[B] +def toIterable : Iterable[A] +def toIterator : Iterator[A] +def toList : List[A] +def toMap [T, U] (implicit ev: <:<[A, (T, U)]) : Map[T, U] +def toSeq : Seq[A] +def toSet [B >: A] : Set[B] +def toStream : Stream[A] +def toString () : String +def toTraversable : Traversable[A] ++ +HaJdm0 konvertirati Map u Array. Možete dobiti Array Key Value pairs. + +
+scala> Map(1 -> 2).toArray +res41: Array[(Int, Int)] = Array((1,2)) ++ +h3. Iterable + +Dodaje pristup iterator-u. + +
+ def iterator: Iterator[A] ++ +Što vam Iterator pruža? + +
+def hasNext(): Boolean +def next(): A ++ +Ovo je vrlo Java like. Nećete vidjeti da se iteratori često koriste u Scali, prije ćete vidjeti funkcionalne kombinatore. + +h3. Set + +
+ def contains(key: A): Boolean + def +(elem: A): Set[A] + def -(elem: A): Set[A] ++ +h3. Map + +Sekvenca ključeva i vrijednosti sa pretraživanjem po ključu. + +Proslijedite List of Pairs u apply() na način: + +
+scala> Map("a" -> 1, "b" -> 2) +res0: scala.collection.immutable.Map[java.lang.String,Int] = Map((a,1), (b,2)) ++ +Ili nešto kao: + +
+scala> Map(("a", 2), ("b", 2)) +res0: scala.collection.immutable.Map[java.lang.String,Int] = Map((a,2), (b,2)) ++ +h6. Digresija + +Što je
->
? To nije specijalna sintaksa, to je metoda koja vraća Tuple.
+
++scala> "a" -> 2 + +res0: (java.lang.String, Int) = (a,2) ++ +Zapamtite, to je samo šugar za + +
+scala> "a".->(2) + +res1: (java.lang.String, Int) = (a,2) ++ +Također možete kreirati jedan sa
++
+
++scala> Map.empty ++ List(("a", 1), ("b", 2), ("c", 3)) +res0: scala.collection.immutable.Map[java.lang.String,Int] = Map((a,1), (b,2), (c,3)) ++ +h3. Često korištene podklase + +*HashSet i HashMap* Brzi pregled, najkorištenije forme tih kolekcija. "HashSet API":http://www.scala-lang.org/api/current/scala/collection/immutable/HashSet.html, "HashMap API":http://www.scala-lang.org/api/current/scala/collection/immutable/HashMap.html + +*TreeMap* Podklasa SortedMap, pruža nam posložen pristup (ordered access). "TreeMap API":http://www.scala-lang.org/api/current/scala/collection/immutable/TreeMap.html + +*Vector* Brza nasumična selekcija i brze nadogradnje. "Vector API":http://www.scala-lang.org/api/current/scala/collection/immutable/Vector.html + +
+scala> IndexedSeq(1, 2, 3) +res0: IndexedSeq[Int] = Vector(1, 2, 3) ++ +*Range* Orderirana sekvenca Int-ova koja je razlomljena. Često ćete ovo vidjeti tamo gdje je for-loop prije korišten. "Range API":http://www.scala-lang.org/api/current/scala/collection/immutable/Range.html + +
+scala> for (i <- 1 to 3) { println(i) } +1 +2 +3 ++ +Range-vi imaju dostupne standardne funkcionalne kombinatore. + +
+scala> (1 to 3).map { i => i } +res0: scala.collection.immutable.IndexedSeq[Int] = Vector(1, 2, 3) ++ +h3. Default + +Korištenje apply metode na trait-ovima će vam dati instancu standardne implementacije, Npr. Iterable(1, 2) vraća List kao standardnu implementaciju. + +
+scala> Iterable(1, 2) + +res0: Iterable[Int] = List(1, 2) ++ +Isto kao sa Seq, kao što smo vidjeli ranije + +
+scala> Seq(1, 2) +res3: Seq[Int] = List(1, 2) + +scala> Iterable(1, 2) +res1: Iterable[Int] = List(1, 2) + +scala> Sequence(1, 2) +warning: there were deprecation warnings; re-run with -deprecation for details +res2: Seq[Int] = List(1, 2) ++ +Set + +
+scala> Set(1, 2) +res31: scala.collection.immutable.Set[Int] = Set(1, 2) ++ +h3. Neki opisni trait-ovi + +*IndexedSeq* brzi nasumični-pristup (random-access) elementima i brze length operacije. "API doc":http://www.scala-lang.org/api/current/scala/collection/IndexedSeq.html + +*LinearSeq* brzi pristup samo prvim elementima putem head-a, ali također posjeduje brzu tail operaciju. "API doc":http://www.scala-lang.org/api/current/scala/collection/LinearSeq.html + +h4. Mutable vs. Immutable + +immutable + +Prednosti +* Ne može se mijenjati u višestrukim dretvama + +Mane +* Ne može se uopće mijenjati + +Scala nam dopušta da budemo pragmatični, pruža nam immutability ali nas ne kažnjava zato što trebamo mutability. Ovo je vrlo slično var vs. val. Uvijek započinjemo sa val i pomičemo se prema var kada je to potrebno. + +Više volimo započeti sa immutable verzijama kolekcija ali se također volimo prebaciti na mutable ako performanse tako diktiraju, yeah. Korištenje immutable kolekcija znači da nećete slučajno promijeniti stvari u više dretvi. + +h2(#mutable). Mutable + +Sve dosad spomenute klase su immutable. Sad pogledajmo mutable kolekcije. + +*HashMap* definira @getOrElseUpdate@, @+=@ "HashMap API":http://www.scala-lang.org/api/current/scala/collection/mutable/HashMap.html + +
+scala> val numbers = collection.mutable.Map(1 -> 2) +numbers: scala.collection.mutable.Map[Int,Int] = Map((1,2)) + +scala> numbers.get(1) +res0: Option[Int] = Some(2) + +scala> numbers.getOrElseUpdate(2, 3) +res54: Int = 3 + +scala> numbers +res55: scala.collection.mutable.Map[Int,Int] = Map((2,3), (1,2)) + +scala> numbers += (4 -> 1) +res56: numbers.type = Map((2,3), (4,1), (1,2)) ++ +*ListBuffer i ArrayBuffer* Definira @+=@ "ListBuffer API":http://www.scala-lang.org/api/current/scala/collection/mutable/ListBuffer.html, "ArrayBuffer API":http://www.scala-lang.org/api/current/scala/collection/mutable/ArrayBuffer.html + +*LinkedList i DoubleLinkedList* "LinkedList API":http://www.scala-lang.org/api/current/scala/collection/mutable/LinkedList.html, "DoubleLinkedList API":http://www.scala-lang.org/api/current/scala/collection/mutable/DoubleLinkedList.html + +*PriorityQueue* "API doc":http://www.scala-lang.org/api/current/scala/collection/mutable/PriorityQueue.html + +*Stack and ArrayStack* "Stack API":http://www.scala-lang.org/api/current/scala/collection/mutable/Stack.html, "ArrayStack API":http://www.scala-lang.org/api/current/scala/collection/mutable/ArrayStack.html + +*StringBuilder* Zanimljivo, StringBuilder je kolekcija. "API doc":http://www.scala-lang.org/api/current/scala/collection/mutable/StringBuilder.html + +h2(#java). Život sa Java-om + +Lako se možete pomicati između Java i Scala tipova kolekcija koristeći konverzije koje su dostupne na JavaConverters package. To povezuje uobičajeno korištene Java kolekcije sa
asScala
metodama i Scala kolekcije sa asJava
metodama.
+
++ import scala.collection.JavaConverters._ + val sl = new scala.collection.mutable.ListBuffer[Int] + val jl : java.util.List[Int] = sl.asJava + val sl2 : scala.collection.mutable.Buffer[Int] = jl.asScala + assert(sl eq sl2) ++ +Dvosmjerne konverzije: + +
+scala.collection.Iterable <=> java.lang.Iterable +scala.collection.Iterable <=> java.util.Collection +scala.collection.Iterator <=> java.util.{ Iterator, Enumeration } +scala.collection.mutable.Buffer <=> java.util.List +scala.collection.mutable.Set <=> java.util.Set +scala.collection.mutable.Map <=> java.util.{ Map, Dictionary } +scala.collection.mutable.ConcurrentMap <=> java.util.concurrent.ConcurrentMap ++ +Dodatno, pružene su i sljedeće jednosmjerne konverzije: + +
+scala.collection.Seq => java.util.List +scala.collection.mutable.Seq => java.util.List +scala.collection.Set => java.util.Set +scala.collection.Map => java.util.Map +diff --git a/web/hr/collections.textile b/web/hr/collections.textile new file mode 100644 index 00000000..1163fb02 --- /dev/null +++ b/web/hr/collections.textile @@ -0,0 +1,424 @@ +--- +prev: basics2.textile +next: pattern-matching-and-functional-composition.textile +title: Kolekcije +layout: post +--- + +Ova lekcija sadržava: + +* Osnovne strukture podataka +** "Arrays":#Arrays +** "Liste":#Lists +** "Sets":#Sets +** "Tuple":#Tuple +** "Mape":#Maps +** "Option":#Option +* Funkcionalni Kombinatori +** "map":#map +** "foreach":#foreach +** "filter":#filter +** "zip":#zip +** "partition":#partition +** "find":#find +** "drop and dropWhile":#drop +** "foldRight and foldLeft":#fold +** "flatten":#flatten +** "flatMap":#flatMap +** "Generalizirani funkcionalni kombinatori":#generalized +** "Map?":#vsMap + +h1. Osnovne strukture podataka + +Scala pruža neke lijepe kolekcije. + +*Dobro je pogledati* Efektivna Scala ima mišljenje kako koristiti collections. + +h2(#Arrays). Arrays + +Array-evi održavaju order, mogu sadržavati duplikate, i mutable su. + +
+scala> val numbers = Array(1, 2, 3, 4, 5, 1, 2, 3, 4, 5) +numbers: Array[Int] = Array(1, 2, 3, 4, 5, 1, 2, 3, 4, 5) + +scala> numbers(3) = 10 ++ +h2(#Lists). Liste + +Liste sadržavaju order, mogu sadržavati duplikate, i immutable su. + +
+scala> val numbers = List(1, 2, 3, 4, 5, 1, 2, 3, 4, 5) +numbers: List[Int] = List(1, 2, 3, 4, 5, 1, 2, 3, 4, 5) + +scala> numbers(3) = 10 ++ +h2(#Sets). Sets + +Set-ovi ne održavaju order i nemaju duplikata + +:9: error: value update is not a member of List[Int] + numbers(3) = 10 +
+scala> val numbers = Set(1, 2, 3, 4, 5, 1, 2, 3, 4, 5) +numbers: scala.collection.immutable.Set[Int] = Set(5, 1, 2, 3, 4) ++ +h2(#Tuple). Tuple + +Tuple grupira jednostavne logičke kolekcije item-a bez korištenja klase. + +
+scala> val hostPort = ("localhost", 80) +hostPort: (String, Int) = (localhost, 80) ++ +Naspram case klasa, nemaju imenovane akcesore (accessors), umjesto toga imaju akcesore imenovane prema njihovoj poziciji i 1-based su, a ne 0-based. + +
+scala> hostPort._1 +res0: String = localhost + +scala> hostPort._2 +res1: Int = 80 ++ +Tuple-ovi fitaju sa pattern matching vrlo lipo. + +
+hostPort match { + case ("localhost", port) => ... + case (host, port) => ... +} ++ +Tuple ima specijalni umak za kreiranje Tuple-ova od 2 vrijednosti:
->
+
++scala> 1 -> 2 +res0: (Int, Int) = (1,2) ++ +*Dobro je pogledati* Efektivna Scala ima mišljenje destructuring bindings ("odpakiranje" tuple-a). + +h2(#Maps). Mape + +Mogu sadržavati osnovne tipove podataka. + +
+Map(1 -> 2) +Map("foo" -> "bar") ++ +Ovo izgleda kao specijalna sintaksa ali sjetite se naše diskusije gdje se Tuple
->
može koristiti za kreiranje Tuple-ova.
+Map() također koristi taj varijabilni argument koji smo naučili u lekciji #1: Map(1 -> "one", 2 -> "two")
koji se proširuje u Map((1, "one"), (2, "two"))
sa prvim elementom koji je ključ i drugim elementom koji je vrijednost Map-e.
+
+Mape same mogu sadržavati Mape ili čak i funkcije kao vrijednosti.
+
++Map(1 -> Map("foo" -> "bar")) ++ +
+Map("timesTwo" -> { timesTwo(_) }) ++ +h2(#Option). Option + +
Option
je kontenjer koji može ali ne mora sadržavati nešto.
+
+Klasičan interface za Option izgleda:
+
++trait Option[T] { + def isDefined: Boolean + def get: T + def getOrElse(t: T): T +} ++ +Option je sam po sebi generičan i ima dvije podklase:
Some[T]
ili None
+
+Pogledajmo primjer kako se Option koristi:
+
+Map.get
koristi Option
za njegov povratni tip.
+Option nam govori da metoda možda neće vratiti ono što ste tražili.
+
++scala> val numbers = Map("one" -> 1, "two" -> 2) +numbers: scala.collection.immutable.Map[java.lang.String,Int] = Map(one -> 1, two -> 2) + +scala> numbers.get("two") +res0: Option[Int] = Some(2) + +scala> numbers.get("three") +res1: Option[Int] = None ++ +Sada se čini da su naši podaci zapeli u
Option
. Kako se nosimo s tim?
+
+Na prvu, pomislili bi da trebate napraviti nešto kondicionalno bazirano na isDefined
metodi.
+
++// We want to multiply the number by two, otherwise return 0. +val result = if (res1.isDefined) { + res1.get * 2 +} else { + 0 +} ++ +Mi bi vam preporučili da koristite ili
getOrElse
ili pattern matching kako bi uspješno baratali rezultatom.
+
+getOrElse
vam dopušta da jednostavno definirate standardnu vrijednost.
+
++val result = res1.getOrElse(0) * 2 ++ +Pattern matching fita prirodno sa
Option
.
+
++val result = res1 match { + case Some(n) => n * 2 + case None => 0 +} ++ +*Dobro je pogledati* Effective Scala ima mišljenje o Options. + +h1(#combinators). Funkcionalni Kombinatori + +
List(1, 2, 3) map squared
pridružuje funkciju squared
elementima liste, vraćajući novu listu, vjerojatno List(1, 4, 9)
. Operacije kao map
zovemo kombinatori. (Ako želite bolju definiciju vjerojatno će vas zanimati Explanation of combinators na Stackoverflow-u.) Njihova najčešća primjena je na standardnim strukturama podataka.
+
+h2(#map). map
+
+Evaulira funkciju kroz svaki element liste, vraćajući listu sa istim brojem elemenata.
+
++scala> val numbers = List(1, 2, 3, 4) +numbers: List[Int] = List(1, 2, 3, 4) + +scala> numbers.map((i: Int) => i * 2) +res0: List[Int] = List(2, 4, 6, 8) ++ +ili proslijedite u funkciju (Scala kompajler automatski konvertira našu metodu u funkciju) + +
+ +scala> def timesTwo(i: Int): Int = i * 2 +timesTwo: (i: Int)Int + +scala> numbers.map(timesTwo) +res0: List[Int] = List(2, 4, 6, 8) ++ +h2(#foreach). foreach + +foreach je kao mapa ali ne vraća ništa, nada. foreach je namijenjen samo za side-effects. + +
+scala> numbers.foreach((i: Int) => i * 2) ++ +vraća ništa. + +Možete pokušati pohraniti return u vrijednost ali to će biti tipa Unit (tj. void) + +
+scala> val doubled = numbers.foreach((i: Int) => i * 2) +doubled: Unit = () ++ +h2(#filter). filter + +uklanja sve elemente gdje funkcija koju proslijeđujete očekuje false. Funkcije koje pozivaju Boolean se obično zovu predikatne funkcije. + +
+scala> numbers.filter((i: Int) => i % 2 == 0) +res0: List[Int] = List(2, 4) ++ +
+scala> def isEven(i: Int): Boolean = i % 2 == 0 +isEven: (i: Int)Boolean + +scala> numbers.filter(isEven) +res2: List[Int] = List(2, 4) ++ +h2(#zip). zip + +zip agregira sadržaj dvije liste u jednu listu parova. + +
+scala> List(1, 2, 3).zip(List("a", "b", "c")) +res0: List[(Int, String)] = List((1,a), (2,b), (3,c)) ++ +h2(#partition). partition + +
partition
dijeli listu ovisno o tome gdje dolazi do pucanja s respektom prema predikatnoj funkciji.
+
++scala> val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) +scala> numbers.partition(_ % 2 == 0) +res0: (List[Int], List[Int]) = (List(2, 4, 6, 8, 10),List(1, 3, 5, 7, 9)) ++ +h2(#find). find + +find vraća prvi element kolekcije koja match-a predikatnu funkciju. + +
+scala> numbers.find((i: Int) => i > 5) +res0: Option[Int] = Some(6) ++ +h2(#drop). drop & dropWhile + +
drop
dropa prvih i elemenata
+
++scala> numbers.drop(5) +res0: List[Int] = List(6, 7, 8, 9, 10) ++ +
dropWhile
uklanja prve elemente koji match-iraju predikatnu funkciju. Npr. ako koristimo dropWhile
kako bi uklonili neobične brojeve iz naše liste brojeva, 1
je drop-an (ali ne i 3
koji je "zaštićen" sa 2
).
+
++scala> numbers.dropWhile(_ % 2 != 0) +res0: List[Int] = List(2, 3, 4, 5, 6, 7, 8, 9, 10) ++ +h2(#fold). foldLeft + +
+scala> numbers.foldLeft(0)((m: Int, n: Int) => m + n) +res0: Int = 55 ++ +0 je početna vrijednost (zapamtite da su brojevi List[Int]), i m +se ponaša kao akumulator. + +Vizualno: + +
+scala> numbers.foldLeft(0) { (m: Int, n: Int) => println("m: " + m + " n: " + n); m + n } +m: 0 n: 1 +m: 1 n: 2 +m: 3 n: 3 +m: 6 n: 4 +m: 10 n: 5 +m: 15 n: 6 +m: 21 n: 7 +m: 28 n: 8 +m: 36 n: 9 +m: 45 n: 10 +res0: Int = 55 ++ +h3. foldRight + +Je isto kao i foldLeft osim što se pokreće u suprotnom smjeru. + +
+scala> numbers.foldRight(0) { (m: Int, n: Int) => println("m: " + m + " n: " + n); m + n } +m: 10 n: 0 +m: 9 n: 10 +m: 8 n: 19 +m: 7 n: 27 +m: 6 n: 34 +m: 5 n: 40 +m: 4 n: 45 +m: 3 n: 49 +m: 2 n: 52 +m: 1 n: 54 +res0: Int = 55 ++ +h2(#flatten). flatten + +flatten lomi jednu razinu nested strukture. + +
+scala> List(List(1, 2), List(3, 4)).flatten +res0: List[Int] = List(1, 2, 3, 4) ++ +h2(#flatMap). flatMap + +flatMap je često korišten kombinator koji kombinira mapping i flattening. flatMap uzima funkciju koja radi sa nested listama i zatim ponovno spaja rezultat. + +
+scala> val nestedNumbers = List(List(1, 2), List(3, 4)) +nestedNumbers: List[List[Int]] = List(List(1, 2), List(3, 4)) + +scala> nestedNumbers.flatMap(x => x.map(_ * 2)) +res0: List[Int] = List(2, 4, 6, 8) ++ +Mislite na to kao na kraticu za mapping i zatim flattening: + +
+scala> nestedNumbers.map((x: List[Int]) => x.map(_ * 2)).flatten +res1: List[Int] = List(2, 4, 6, 8) ++ +taj primjer koji poziva map i zatim flatten je primjer "kombinator"-like prirode tih funkcija. + +*Dobro je pogledati* Efektivna Scala ima mišljenje o tome flatMap. + +h2(#generalized). Generalizirani funkcijski kombinatori + +Sada smo naučili osnovne funkcije za rad sa kombinatorima. + +Sada želimo sami napisati naše funkcijske kombinatore. + +Zanimljivo, svaki funkcijski kombinator prikazan ranije može biti napisan na vrhu fold-a. Pogledajmo neke primjere. + +
+def ourMap(numbers: List[Int], fn: Int => Int): List[Int] = { + numbers.foldRight(List[Int]()) { (x: Int, xs: List[Int]) => + fn(x) :: xs + } +} + +scala> ourMap(numbers, timesTwo(_)) +res0: List[Int] = List(2, 4, 6, 8, 10, 12, 14, 16, 18, 20) ++ +Zašto List[Int]()? Scala nije bila dovoljno pametna i nije shvatila da želite praznu listu Int-ova u koju bi to akumulirali. + +h2(#vsMap). Map? + +Svi prikazani funkcijski kombinatori također rade na Maps. Maps se može promatrati kao lista parova kako bi funkcije koje ste pisali radile na paru ključeva i vrijednosti u Map. + +
+scala> val extensions = Map("steve" -> 100, "bob" -> 101, "joe" -> 201) +extensions: scala.collection.immutable.Map[String,Int] = Map((steve,100), (bob,101), (joe,201)) ++ +Sada filter-irajte svaki zapis čija je ekstenzija broja manja od 200. + +
+scala> extensions.filter((namePhone: (String, Int)) => namePhone._2 < 200) +res0: scala.collection.immutable.Map[String,Int] = Map((steve,100), (bob,101)) ++ +Zato što vam daje tuple, morate povući ključeve i vrijednosti sa njihovim pozicijskim akcesorima. Sretno! + +Srećom za nas, zapravo možemo koristiti pattern match za lipo izvlačenje ključeva i vrijednosti. + +
+scala> extensions.filter({case (name, extension) => extension < 200}) +res0: scala.collection.immutable.Map[String,Int] = Map((steve,100), (bob,101)) ++ +Zašto ovo radi? Zašto možete proslijediti parcijalni pattern match? + +Vidimo se sljedeći tjedan! diff --git a/web/hr/concurrency.textile b/web/hr/concurrency.textile new file mode 100644 index 00000000..549333f7 --- /dev/null +++ b/web/hr/concurrency.textile @@ -0,0 +1,474 @@ +--- +prev: specs.textile +next: java.textile +title: Konkurentnost u Scali +layout: post +--- + +* "Runnable/Callable":#runnable +* "Dretve":#Thread +* "Executors/ExecutorService":#executor +* "Futures":#Future +* "Sigurnosni problem dretvi":#danger +* "Npr. - Search Engine":#example +* "Solutions":#solutions + +h2(#runnable). Runnable/Callable + +Runnable ima single metodu koja ne vraća vrijednost. + +
+trait Runnable { + def run(): Unit +} ++ +Callable je sličan, ali vraća vrijednost + +
+trait Callable[V] { + def call(): V +} ++ + +h2(#Thread). Dretve + +Scala konkurentnost je izgrađena na temelju Java concurrency modela. + +Na Oracle(Sun) JVM-ovima, sa velikim IO opterećenjem, možemo pokretati tisuće dretvi na jednoj mašini. + +Thread uzima Runnable. Morate pozvati @start@ na Thread kako bi mogao pokrenuti Runnable. + +
+scala> val hello = new Thread(new Runnable { + def run() { + println("hello world") + } +}) +hello: java.lang.Thread = Thread[Thread-3,5,main] + +scala> hello.start +hello world + ++ +Kada vidite klasu koja implementira Runnable, znate da je namijenjena pokretanju u dretvi. + +h3. Nešto single-dretvovano + +Slijedi dio koda koji radi ali ima problema, hm, tko zna što mu je... + +
+import java.net.{Socket, ServerSocket} +import java.util.concurrent.{Executors, ExecutorService} +import java.util.Date + +class NetworkService(port: Int, poolSize: Int) extends Runnable { + val serverSocket = new ServerSocket(port) + + def run() { + while (true) { + // This will block until a connection comes in. + val socket = serverSocket.accept() + (new Handler(socket)).run() + } + } +} + +class Handler(socket: Socket) extends Runnable { + def message = (Thread.currentThread.getName() + "\n").getBytes + + def run() { + socket.getOutputStream.write(message) + socket.getOutputStream.close() + } +} + +(new NetworkService(2020, 2)).run ++ +Svaki zahtjev će se odazivati na trenutnu dretvu, koja je uvijek @main@. + +Glavna mana ovog koda je da samo jedan zahtjev može dobiti odgovor! + +Možete svaki zahtjev staviti u dretvu. Jednostavno zamijenite + +
+(new Handler(socket)).run() ++ +sa + +
+(new Thread(new Handler(socket))).start() ++ +Ali što ako želite ponovno iskoristiti dretve ili želite nešto drugo s njima? + + +h2(#executor). Executori + +Sa izlaskom Jave 5 odlučeno je da je potrebno više abstraktnih interface-a prema dretvama. + +Možete dobiti @ExecutorService@ koristeći statičke metode na @Executors@ objektima. Te metode vam pružaju mogućnost konfiguracije na @ExecutorService@ sa raznim stvarima kao što je thread pooling. + +Slijedi reorganizirani old blocking network server primjer napisan sa mogućnošću konkuretnih zahtjeva. + +
+import java.net.{Socket, ServerSocket} +import java.util.concurrent.{Executors, ExecutorService} +import java.util.Date + +class NetworkService(port: Int, poolSize: Int) extends Runnable { + val serverSocket = new ServerSocket(port) + val pool: ExecutorService = Executors.newFixedThreadPool(poolSize) + + def run() { + try { + while (true) { + // This will block until a connection comes in. + val socket = serverSocket.accept() + pool.execute(new Handler(socket)) + } + } finally { + pool.shutdown() + } + } +} + +class Handler(socket: Socket) extends Runnable { + def message = (Thread.currentThread.getName() + "\n").getBytes + + def run() { + socket.getOutputStream.write(message) + socket.getOutputStream.close() + } +} + +(new NetworkService(2020, 2)).run ++ +U primjeru koji slijedi nalazi se zapis povezivanja na prethodni primjer i vidimo kako se interne dretve ponovno iskorištavaju. + +
+$ nc localhost 2020 +pool-1-thread-1 + +$ nc localhost 2020 +pool-1-thread-2 + +$ nc localhost 2020 +pool-1-thread-1 + +$ nc localhost 2020 +pool-1-thread-2 ++ + +h2(#Future). Futures + +@Future@ predstavlja asinkronu komputaciju. Možete wrap-ati vašu komputaciju u Future i kada trebate rezultat, jednostavno možete pozvati blocking @get()@ metodu na njoj. @Executor@ vraća @Future@. Ako koristite Finagle RPC sustav, možete koristiti @Future@ instance kako bi pohranili rezultate koji još možda nisu stigli. + +@FutureTask@ je Runnable i dizajniran je kako bi se pokretao sa @Executor@ + +
+val future = new FutureTask[String](new Callable[String]() { + def call(): String = { + searcher.search(target); +}}) +executor.execute(future) ++ +Sadas trebamo rezultate pa krenimo. + +
+val blockingResult = future.get() ++ +*Dobro je pogledati* Scala School's Finagle stranica ima pregršt primjera korištenja
Future
, uključujući neke lijepe načine za njihovu kombinaciju. Efektivna Scala ima mišljenje o tome Futures .
+
+h2(#danger). Sigurnosni problem dretvi
+
++class Person(var name: String) { + def set(changedName: String) { + name = changedName + } +} ++ +Ovaj program nije siguran u multi-thread okruženju. Ako dvije dretve imaju reference na istu instancu Person i zovu @set@, ne možete predvidjeti koji @name@ će biti na kraju oba poziva. + +U Java memory modelu, svaki procesor ima dopuštenje spremiti vrijednosti u svoj L1 ili L2 cache kako bi dvije dretve koje se pokreću na različitim procesorima imale svoj pogled na podatke. + +Pogledajmo neke alate koji forsiraju dretve da zadrže konzistentan pogled na podatke. + +h3. Tri alata + +h4. synchronization + +Mutex-i pružaju ownership semantiku. Kada uđete u mutex, posjedujete ga. Najčešći način korištenja mutex-a u JVM-u je sinkronizacija na nešto. U ovom slučaju sinkronizirati ćemo nad našom Person. + +U JVM-u možete sinkronizirati nad svakom instancom koja nije null. + +
+class Person(var name: String) { + def set(changedName: String) { + this.synchronized { + name = changedName + } + } +} ++ +h4. volatile + +Sa Java 5 promjenom memory modela, volatile i synchronized su gotovo identični osim što sa volatile, null-ovi su dopušteni. + +@synchronized@ dopušta finije zaključavanje. @volatile@ sinkronizira nad svakim pristupom. + +
+class Person(@volatile var name: String) { + def set(changedName: String) { + name = changedName + } +} ++ +h4. AtomicReference + +Također sa Java 5 je dodan cijeli niz low-level concurrency primitiva. Jedan od njih je @AtomicReference@ klasa. + +
+import java.util.concurrent.atomic.AtomicReference + +class Person(val name: AtomicReference[String]) { + def set(changedName: String) { + name.set(changedName) + } +} ++ +h4. Košta li nas ovo? + +@AtomicReference je najskuplji od navedenih izbora budući da morate proći metode kako bi pristupili vrijednostima. + +@volatile@ i @synchronized@ su građeni na Java built-in monitorima. Monitori koštaju malo ako nema konekcija. Budući da @synchronized@ dopušta finiju kontrolu nad čime sinkronizirate, potrošiti ćemo manje budući da @synchronized@ teži biti najjeftinije rješenje. + +Kada uđete u sinkronizirane točke, pristupne volatile reference, ili deferencirane AtomicReference, Java forsira procesor da flush-a svoje cache linije i pruža konzistentan pogled na podatke. + +MOLIM VAS, ISPRAVITE ME AKO SAM U KRIVU. Ovo je složena tema i vjerojatno svatko ima svoje mišljenje o tome. + +h3. Drugi lipi alati iz Java 5 + +Kao što smo spomenuli sa @AtomicReference@, Java 5 pruža sjajne alate sa njim. + + +h4. CountDownLatch + +@CountDownLatch@ je jednostavan mehanizam za višestruke dretve koji služi za njihovu međusobnu komunikaciju. + +
+val doneSignal = new CountDownLatch(2) +doAsyncWork(1) +doAsyncWork(2) + +doneSignal.await() +println("both workers finished!") ++ +Među ostalim, to je odlično za unit testove. Recimo da radite neki asinkronirani posao i želite osigurati da se fukcije izvršavaju. Jednostavno izvršite @countDown@ nad vašim funkcijama i @await@ for it. + +h4. AtomicInteger/Long + +Budući da je inkrementiranje Int-ova i Long-ova česta operacija dodani su @AtomicInteger@ i @AtomicLong@. + +h4. AtomicBoolean + +Jasno samo po sebi. + +h4. ReadWriteLocks + +@ReadWriteLock@ vam dopušta 'hvatanje' reader i writer lock-ova. reader lock-a samo blok kada je writer lock zauzet. + +h2(#example). Kreirajmo nesigurni search engine + +Ovdje je jednostavni invert-ani index koji nije thread-safe. Naš invert-ani index map-ira dijelove imena danog User-a. + +Ovo je napisano na naivan način pretpostavljajući samo single-thread pristup. + +Primijetite alternativni standardni konstruktor @this()@ koji koristi @mutable.HashMap@ + +
+import scala.collection.mutable + +case class User(name: String, id: Int) + +class InvertedIndex(val userMap: mutable.Map[String, User]) { + + def this() = this(new mutable.HashMap[String, User]) + + def tokenizeName(name: String): Seq[String] = { + name.split(" ").map(_.toLowerCase) + } + + def add(term: String, user: User) { + userMap += term -> user + } + + def add(user: User) { + tokenizeName(user.name).foreach { term => + add(term, user) + } + } +} ++ +Izostavili smo kako izvući korisnike iz našeg index-a zasada. O tome kasnije. + +h2(#solutions). Napravimo ga sigurnim + +U našem inverted index primjeru, userMap nije zasigurno siguran. Više klijenata može pokušati dodati item-e u isto vrijeme i imati isti način pregleda pogrešaka koji smo vidjeli u našem prvom @Person@ primjeru. + +Budući da userMap nije thread-safe, kako zadržati samo jednu dretvu kada je mutiramo u isto vrijeme? + +Možete pokušati lock-irati na userMap dok dodajete. + +
+def add(user: User) { + userMap.synchronized { + tokenizeName(user.name).foreach { term => + add(term, user) + } + } +} ++ +Nažalost, to je previše sirovo. Uvijek pokušajte napraviti što više skupog posla izvan mutex-a što je više moguće. Zapamtite što smo rekli kako je lock-anje jeftino kada nema konflikata. Ako napravite više posla unutar bloka, biti će manje konflikata. + +
+def add(user: User) { + // tokenizeName was measured to be the most expensive operation. + val tokens = tokenizeName(user.name) + + tokens.foreach { term => + userMap.synchronized { + add(term, user) + } + } +} ++ +h2. SynchronizedMap + +Možemo mix-ati sinkronizaciju sa mutabilnom HashMap koristeći SynchronizedMap trait. + +Možemo extend-ati naš postojeći InvertedIndex kako bi korisnicima dali jednostavan način kreiranja sinkroniziranog index-a. + + +
+import scala.collection.mutable.SynchronizedMap + +class SynchronizedInvertedIndex(userMap: mutable.Map[String, User]) extends InvertedIndex(userMap) { + def this() = this(new mutable.HashMap[String, User] with SynchronizedMap[String, User]) +} ++ +Ako pogledate implementaciju, shvatiti ćete da jednostavno sinkronizira na svakoj metodi, što znači da je sigurno, ali vjerojatno nećete imati performanse koje želite. + +h2. Java ConcurrentHashMap + +Java dolazi sa lipom thread-safe ConcurrentHashMap. Srećom, možemo koristiti JavaConverters kako bi dobili lijepu Scala semantiku. + +Zapravo, možemo smisleno raslojiti naš novi, thread-safe InvertedIndex kao ekstenziju nad starim nesigurnim index-om. + +
+import java.util.concurrent.ConcurrentHashMap +import scala.collection.JavaConverters._ + +class ConcurrentInvertedIndex(userMap: collection.mutable.ConcurrentMap[String, User]) + extends InvertedIndex(userMap) { + + def this() = this(new ConcurrentHashMap[String, User] asScala) +} ++ +h2. Nalodajmo naš InvertedIndex + +h3. Naivan način + +
+ +trait UserMaker { + def makeUser(line: String) = line.split(",") match { + case Array(name, userid) => User(name, userid.trim().toInt) + } +} + +class FileRecordProducer(path: String) extends UserMaker { + def run() { + Source.fromFile(path, "utf-8").getLines.foreach { line => + index.add(makeUser(line)) + } + } +} ++ +Za svaku liniju u našoj datoteci, zovemo @makeUser@ i zatim ga @add@ našem InvertedIndex-u. Ako koristimo konkuretni InvertedIndex, možemo pozvati add u paraleli i budući da makeUser nema nuspojava (side-effects), to je već thread-safe. + +Ne možemo paralelno čitati datoteku ali _možemo_ kreirati User i dodati ga index-u u paraleli. + +h3. Rješenje: Producer/Consumer + +Uobičajeni pattern za async komputaciju je odvojiti producers od consumers i dopustiti im komunikaciju samo putem @Queue@. Pogledajmo kako bi to radilo za naš search engine indexer. + +
+import java.util.concurrent.{BlockingQueue, LinkedBlockingQueue} + +// Concrete producer +class Producer[T](path: String, queue: BlockingQueue[T]) extends Runnable { + def run() { + Source.fromFile(path, "utf-8").getLines.foreach { line => + queue.put(line) + } + } +} + +// Abstract consumer +abstract class Consumer[T](queue: BlockingQueue[T]) extends Runnable { + def run() { + while (true) { + val item = queue.take() + consume(item) + } + } + + def consume(x: T) +} + +val queue = new LinkedBlockingQueue[String]() + +// One thread for the producer +val producer = new Producer[String]("users.txt", q) +new Thread(producer).start() + +trait UserMaker { + def makeUser(line: String) = line.split(",") match { + case Array(name, userid) => User(name, userid.trim().toInt) + } +} + +class IndexerConsumer(index: InvertedIndex, queue: BlockingQueue[String]) extends Consumer[String](queue) with UserMaker { + def consume(t: String) = index.add(makeUser(t)) +} + +// Let's pretend we have 8 cores on this machine. +val cores = 8 +val pool = Executors.newFixedThreadPool(cores) + +// Submit one consumer per core. +for (i <- i to cores) { + pool.submit(new IndexerConsumer[String](index, q)) +} +diff --git a/web/hr/index.html b/web/hr/index.html new file mode 100644 index 00000000..1889d67b --- /dev/null +++ b/web/hr/index.html @@ -0,0 +1,56 @@ +--- +layout: default +title: Scala školica +--- + +
Od ∅ do Distribuiranog Servisa
+Ostali jezici:
+ + +Scala škola je započela kao serija lekcija na Twitter-u kako bi pripremila iskusne inženjere da postanu produktivni Scala programeri. Scala je relativno novi jezik, ali se temelji na mnogim poznatim konceptima. Zbog toga se pretpostavlja da Vi, draga publiko ;), poznajete osnovne koncepte OOP na kojima se temelji Scala. Na taj način ćete brže svladati osnove Scala programskog jezika. +
+ +Smatramo da je najbolje prići učenju Scale ne kao da se radi o poboljšanoj verziji Jave, nego kao da se radi o potpuno novom jeziku. Iskustvo u Javi se ne očekuje. Fokus će biti na interpreter-u i na objektno-funkcijskom stilu kao i na stilu programiranja. Naglasak će biti na održivosti, jasnoći izraza i utjecaju type sustava.
+Većina lekcija zahtjeva samo SCALA REPL. Čitatelja se potiče da prati lekcije, ali i da dodatno proširuje svoje znanje o Scali! Koristite ove lekcije kao početnu točku za daljnje istraživanje Scale.
+ +Dodatni sadržaji za učenje: +