wtorek, 11 listopada 2014

Nauka Scali i Javy 8

Zanim przejdziemy do pierwszego spotkania-warsztatów przeznaczonych nauce Scali tylko i wyłącznie - wpierw kącik społecznościowy

Kącik społecznościowy

  • W najbliższa sobotę CodeRetreat - zapisy pod linkiem po prawej ---> link po prawej(zapisy) . Niby tam lista jest zamknięta ale dodajcie się na "waitlistę" bo kilka osób jeszcze pewnie zrezygnuje. W tym roku zapraszaliśmy speców z zagranicy do poprowadzenia wydarzenia i chociaż ich nie będzie to przysłali wskazówki jak tam to wydarzenie lepiej poprowadzić
    • Start by asking people what they would like to learn
    • Write down what they say on a flipchart or post-its
    • Group them into categories, and write down the number of "votes" for each category
    • Usually, you get things such as: pair programming, something new, TDD, Refactoring
    • If there are first timers, start with the normal free session that helps familiarize with the problem. Otherwise you can skip it
    • Then pick the categories in the order of votes and select the constraints that answer to those categories. You might need to improvise or ask for help - Adi and I will be available to help
  • A 20 listopada na DMCS odbędzie się wykład Michał Balińskiego o Amazon AWS. Michał jest na tę chwilę chyba najlepszym specjalistą w tych tematach w Łodzi - także na tę chwilę zapisało się już 40 osób i wy zapiszcie się także w linku obok Link obok do zapisów. Może pobijemy rekord publiczności. Obecny chyba nalezy do Grześka Borkowskiego do którego swego czasu przyszło około 70 osób na prezentacje o Javascript
  • .

Pierwsze spotkanie Nauki Scali

Scali warto się uczyć i jestem w pełni świadom tego, że są ludzie którzy pałają do tego języka nienawiścią (sam kiedyś tam byłem) ale jak ktoś nie chce nie musi używać natomiast możliwości edukacyjne są moim zdaniem ogromne :

  • REPL i worksheet są cudowne jeśli chodzi o prezentację uczestnikom tego co dzieje w kodzie. Nie trzeba printlainami iterować po elementach listy bo jest ona niejako "prześwietlana" Jeśli jest coś takiego w Javie to ja nie znaju.
  • Sam zacząłem interesować się scalą i programowaniem pod kątem nauki programowania funkcyjnego zanim Java 8 była gotowa i jeśli chodzi o ten temat to jest on dużo przejrzystszy w scali : funkcja to funkcja bez względu na to ile ma parametrów - gdzie w javie to programowanie funkcyjne jest zrobione najbardziej obiektowo chyba jak można było : Supliery,Consumery, funkcje, bifunkcje (a w dodatku "Updadek Bastionu" będą TriFunkcje i nowe zbroje).
  • W tym języku są koncepcje jak choćby pattern matching, które jak się okazuje (przynajmniej dla mnie było to odkrycie) są także nie obce wielu innym językom. Zamiast czekać nim pojawią się w Javie można z nimi zapoznać się już teraz.
  • I ostatni - moim zdaniem najważniejszy strategicznie punkt - generując anomalię edukacyjną polegającą na wzajemnej nauce relatywnie zaawansowanej technologi bez - (uwaga to bardzo ważne) - bez jasnego zysku komercyjnego - można przerwać zjawisko takiej "łódzkiej samo nakręcającej się spirali gówna" (rysunek dzięki uprzejmości mojej poniżej)

Jeśli ludzie nie byli tylko zwyczajowo mili to ogólnie się podobało. Generalnie idea jest taka aby pojawiła się jakaś grupa ludzi chcąca prowadzić warsztaty i to całe przedsięwzięcie się skalowało - są już chętni także następnym razem poprowadzi ktoś inny itd.

Co należy umieć aby poprowadzić warsztat

Tutaj dam linka sam do siebie - Pewność siebie poprzez niewiedzę. Generalnie najlepszą odpowiedzią co trzeba zrobić aby poprowadzić warsztat ze scali - należy nie wiedzieć, że nie ma się odpowiedniej wiedzy aby go poprowadzić. A później trzeba się tylko zgłosić, opracować materiał i przećwiczyć warsztat aby był ciekawy dla uczestników - i to tylko tyle.

Jak widać sam wykorzystuje tę technikę generując w sobie urojenia, że moje działania jakoś wpłyną na to co się dzieje w tym mieście. Ale dopóki ja mam z tego zabawę a ludziom się podoba i się czegoś uczą to będziemy to robić.

Co więcej

TomTom zaczął się angażować w edukację studentów w Łodzi. Na pierwszych zajęciach mieli okazję poznać trochę Scali i Playframework.

A TERAZ KOD.

Pierwsze porównanie

Na samym początku szybkie porównywanie scali i javy8 pod kątek filtrowania elementów listy przy pomocy lambdy (nie wiem czy to fachowo powinno nazywać się "przetwarzanie batchowe","przetwarzanie strumieniowe"czy też "przetwarzanie deklaratywne"?)

val lista=List(1,2,3,4,5) 
lista.filter(element=>element>2)
zadanie : stworzyć listę elementów 'a','b','c','d','e' i odfiltrować tak by zostało 'd','e'
val lista2=List('a','b','c','d','e')
lista2.filter(e=>e>'c')

Java
List lista = asList(1,2,3,4,5);
// a to nie wystarczy
Stream filter = lista.stream().filter(element->element > 2);
//trzeba tak
List result = lista.stream().filter(element->element > 2).collect(Collectors.toList());

Tworzenie listy

Aby słuchacze się nie nudzili - kilka ciekawych sposobów na inicjację(tudzież tworzenie) listy

List.range(0, 10, 2)
List.fill(7)(42)
List.tabulate(10)(elem=>s"element ${elem}")
"tekst".toList
zadanie : stworzyć listę z kolejnymi kwadratami liczb od 0 do N=20 i zostawić tylko podzielne przez 3
val lista=List.tabulate(20)(e=>e*e)
lista.filter(_ % 3==0)

sortowanie i mapowanie

lista.filter(element=>element>50).sortWith((elem1,elem2)=>elem1>elem2).map(elem=>elem+1)
zadanie : stworzyć listę od 1 do 100, zostawić elementy większe od 50 i posortować tak by parzyste liczby były na początku listy
List.range(1,100).filter(e=>e>50).sortWith((e1,e2)=>e1%2==0)

Java
List result = lista.stream()
    .filter(e->e > 2)
    .sorted(Comparator.reverseOrder())
    .map(e->e+1)
    .collect(Collectors.toList());
  
  result.forEach(System.out::println);
Przy pierwszym zetknięciu się z uproszczeniem zapisu przy pomocy podkreślenia czytelnik może przeżyć szok lecz zauważenie zysków tego zapisu to tylko kwestia czasu i praktyki.
 lista.filter(e=>e>2).sortWith((e1,e2)=>e1>e2).map(e=>e+1)
//lub lepiej
        lista.filter(_>2).sortWith(_>_).map(_+1)

Podzbiory

 val lista=List(1,1,2,1,3,1,4,5) 

lista.takeWhile(_==1)                     //> res0: List[Int] = List(1, 1)
lista.dropWhile(_==1)                     //> res1: List[Int] = List(2, 1, 3, 1, 4, 5)
Stream stream = Stream.of(1,1,2,1,3,1,4,5);
  
  List result = stream
    .limit(5)
    .skip(2)
    .sorted(Comparator.reverseOrder())
    .map(e->e+1)
    .collect(Collectors.toList());
  
  result.forEach(System.out::println);
zadanie : stworzyć listę od 0 do 100 ,posortować po drugiej cyfrze (czyli 19 jest większe od 88) i zostawić początek listy mniejszy od 90.
List.range(0, 100).sortWith((e1,e2)=>(e1%10) > (e2%10)).takeWhile(_<90)

Kilka kolekcji i Flatten

Przy tym cwiczeniu dla zwiększenia satysfakcji uczestników można (a nawet należy) wspomnieć różnicę pomiędzy listą i wektorem.

 val lista=List("elektromagnetyczny","pierun","trzy")
     //Vektor                                             
 val samogloski=Vector('a','e','u','y')    //> samogloski  : scala.collection.immutable.Vector[Char] = Vector(a, e, u, y)
 //Array
 def zostawSamogloski(s:String)=s.toCharArray().filter(samogloski contains(_))
                                                  //> zostawSamogloski: (s: String)Array[Char]

 lista.map(zostawSamogloski)               //> res0: List[Array[Char]] = List(Array(e, e, a, e, y, y), Array(e, u), Array(y
                                                  //| ))
 lista.map(zostawSamogloski).flatten       //> res1: List[Char] = List(e, e, a, e, y, y, e, u, y)
 
 //lub
 lista.flatMap(zostawSamogloski)
zadanie : zdublować przy pomocy flatMap wszystkie elementy listy List(1,2,3) -> List(1,1,2,2,3,3)
List(1,2,3).flatMap(e=>List(e,e))

redukcja

Generalnie redukcję na siłce robi się na wiosnę aby na lato by kaloryfer - tutaj jednak chodzi o redukcję kolekcji do jednej wartości. W poniższym przykładzie będzie również zaprezentowany mały przykład pokazujący różnice pomiędzy metodą i funkcją

val lista=List(1,2,3,4,5)

//redukcja
lista.reduce(_*_) 

//ponowne wykorzystanie funkcji
def add(a:Int,b:Int)=a+b 

lista.reduce(add)  
lista.foldLeft(0)(add)

//ale mozna i
val addf=(a:Int,b:Int)=>a+b  

//jaka jest roznica? Metody moga mieć generyki i na tym etapie tyle tłumaczenia musi wystarczyć :)

val reduceText=(a:Int,b:Int)=>a+":"+b
def reduceTextM[A](a:A,b:A)=a+":"+b

reduceText(1,2)                           //> res1: String = 1:2
reduceText('1','2')                       //> res2: String = 49:50
//BŁĄÐ KOMPILACJI //reduceText("1","2")
reduceTextM(1,2)                          //> res3: String = 1:2
reduceTextM('1','2')                      //> res4: String = 1:2
reduceTextM("1","2")                      //> res5: String = 1:2

lista.reduce(reduceTextM)                 //> res6: Any = 1:2:3:4:5
val lista2=List('a','b','c')                    //> lista2  : List[Char] = List(a, b, c)
lista2.foldLeft("")(reduceTextM)          //> res7: String = :a:b:c

zadanie : treść podobna "zdublować przy pomocy flatMap wszystkie elementy listy List(1,2,3) -> List(1,1,2,2,3,3)" ale tym razem w dwóch wersjach
  1. Funkcji, która przyjmuje listę typu Int i wykonuje wspomnianą operację
  2. Metody, która przyjmuje jakąś listę typu A i wykonuje wspomnianą operację
val double=(l:List[Int])=>l.flatMap(e=>List(e,e))
def doubleM[A](l:List[A])=l.flatMap(e=>List(e,e))

A jak to wygląda w Javie ?
Stream stream = Stream.of(1,1,2,1,3,1,4,5);
  Optional result = stream.reduce((a,b)->a+b);
  result.ifPresent(System.out::println);

Grupowanie

Tutaj zaś zastapimy działanie na intach działaniem na bardziej domenowych obiektach. Zobacyzmy tkaże ile więcej kodu trzeba napisac w Javie by osiągnąc podobny rezultat:(

case class User(name:String,age:Int,gender:String)
 val lista=List(User("Stefan",28,"M"),User("Joanna",20,"F"),User("Elżbieta",28,"F"))
                                                  //> lista  : List[akademia.kolekcje.User] = List(User(Stefan,28,M), User(Joanna,
                                                  //| 20,F), User(Elżbieta,28,F))

 lista.groupBy(_.age)                      //> res0: scala.collection.immutable.Map[Int,List[akademia.kolekcje.User]] = Map
                                                  //| (20 -> List(User(Joanna,20,F)), 28 -> List(User(Stefan,28,M), User(Elżbieta
                                                  //| ,28,F)))
 lista.groupBy(_.gender)                   //> res1: scala.collection.immutable.Map[String,List[akademia.kolekcje.User]] = 
                                                  //| Map(M -> List(User(Stefan,28,M)), F -> List(User(Joanna,20,F), User(Elżbiet
                                                  //| a,28,F)))
eksperymenty :
val random=new java.util.Random()
import Math.abs
val users=List.tabulate(100)(n=>User((n % 30).toString,abs(random.nextInt()) % 30,"F"))
users.groupBy(_.age)
users.groupBy(_.name) 

W javie niestety trzeba się trochę więcej rozpisać :
class User{
 private final String name;
 private final int age;
 public User(String name, int age) {
  this.name = name;
  this.age = age;
 }
 public String getName() {
  return name;
 }
 public int getAge() {
  return age;
 }
 @Override
 public String toString() {
  return "User [name=" + name + ", age=" + age + "]";
 }
}
List<User> users = asList(new User("ccc",12),new User("bbb",20),new User("aaa",12));
Map<Integer, List<User>> result = users.stream().collect(Collectors.groupingBy(User::getAge));

result.forEach((k,vl)->System.out.println(k+":"+vl));

Span i partition

Niech kod przemówi :
val numery=List(1,4,5,78,9,1,2,3)         //> numery  : List[Int] = List(1, 4, 5, 78, 9, 1, 2, 3)
 
 
 numery.partition(_%2==0)                  //> res0: (List[Int], List[Int]) = (List(4, 78, 2),List(1, 5, 9, 1, 3))
 numery.partition(_>3)                     //> res1: (List[Int], List[Int]) = (List(4, 5, 78, 9),List(1, 1, 2, 3))
 numery.partition(_<10)                    //> res3: (List[Int], List[Int]) = (List(1, 4, 5, 9, 1, 2, 3),List(78))
 numery.span(_<10)                         //> res2: (List[Int], List[Int]) = (List(1, 4, 5),List(78, 9, 1, 2, 3))
zadanie : stworzyć listę od 0 do 100 i podzielić ją na dwie listy : w jednej mają być potęgi dwójki a w drugiej liście cała reszta.
import math._
val powersOfTwo=List.range(0, 10).map(n=>pow(2, n).toInt).toSet
List.range(0,100).partition(powersOfTwo contains _)

Widok

Tutaj jest różnica z javą.
val numery=List(1,4,5,78,9,1,2,3)         //> numery  : List[Int] = List(1, 4, 5, 78, 9, 1, 2, 3)
 
// zwrocic uwage na SeqViewMMF 
numery.view.map(_+1).map(_+2).filter(_>4) //> res0: scala.collection.SeqView[Int,Seq[_]] = SeqViewMMF(...)

Zipy i Unzipy

val numery=List.range(13, 27)             //> numery  : List[Int] = List(13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,  26)
numery.zipWithIndex                       //> res0: List[(Int, Int)] = List((13,0), (14,1), (15,2), (16,3), (17,4), (18,5),
                                                  //|  (19,6), (20,7), (21,8), (22,9), (23,10), (24,11), (25,12), (26,13))                                                 
 
 for{
  (e,i) <- numery.zipWithIndex
  } yield s""" """
                                                  //> res1: List[String] = List( ,  ,  ,  ,  ,  ,  ,
                                                  //|   ,  ,  
                                                  //| ,  ,  ,  ,  )
zadanie : stworzyć listę od 0 do 10 i usunąć co trzeci element
val lista=List.range(1,10)
lista.zipWithIndex.filter{case (_,i) => (i+1)%3!=0}.map(_._1)

Mutable i Immutable

val numery=List(1,2,3,4)                  //> numery  : List[Int] = List(1, 2, 3, 4)
 
 numery.+:(5)                              //> res0: List[Int] = List(5, 1, 2, 3, 4)
 numery.+:(5)                              //> res1: List[Int] = List(5, 1, 2, 3, 4)

 
  import scala.collection.mutable.ListBuffer
  val numeryBuffer=ListBuffer(1,2,3,4)            //> numeryBuffer  : scala.collection.mutable.ListBuffer[Int] = ListBuffer(1, 2, 
                                                  //| 3, 4)
  
  numeryBuffer += 5                               //> res2: poligon.poligon.numeryBuffer.type = ListBuffer(1, 2, 3, 4, 5)
  numeryBuffer += 5                               //> res3: poligon.poligon.numeryBuffer.type = ListBuffer(1, 2, 3, 4, 5, 5) 

For

val numbers=Vector(1,2,3,4)
val letters=Vector('a','b','c','d')

for{
  n<-numbers
  l<-letters
 } yield (n,l)

val names = Map("firstname" -> "Roman", "lastname" -> "Kowalski")

for ((k, v) <- names) println(s"key : ${k} , name : ${v}")
eksperymenty :
numbers.map(n=>
    letters.map(e=>s"($n,$e)")
   )                                              //> res1: scala.collection.immutable.Vector[scala.collection.immutable.Vector[St
                                                  //| ring]] = Vector(Vector((1,a), (1,b), (1,c), (1,d)), Vector((2,a), (2,b), (2,
                                                  //| c), (2,d)), Vector((3,a), (3,b), (3,c), (3,d)), Vector((4,a), (4,b), (4,c), 
                                                  //| (4,d)))

   numbers.flatMap(n=>
    letters.map(e=>s"($n,$e)")
   )                                              //> res1: scala.collection.immutable.Vector[String] = Vector((1,a), (1,b), (1,c)
                                                  //| , (1,d), (2,a), (2,b), (2,c), (2,d), (3,a), (3,b), (3,c), (3,d), (4,a), (4,b
                                                  //| ), (4,c), (4,d))

Brak komentarzy:

Prześlij komentarz