22 kwietnia 2012

scala> lazy val days2Euro2012 = 47

Słoneczna niedziela nie sprzyjała głębszej nauce, więc skończyło się na przejrzeniu forum książki o programowaniu funkcyjnym w języku Scala - Functional Programming in Scala. Niewielka liczba wątków zachęcała do krótkiego rekonesansu do Scali przez pryzmat dyskusji w temacie jej funkcyjnej strony.

W Function representation in Scala trafiłem na:
val add:(Int, Int) => Int = (x,y) => x + y
podczas gdy większość przykładów, które do tej pory napotkałem definiowały funkcję jak poniżej:
def add(x: Int, y: Int): Int = x + y
Niby nic odkrywczego, ale przywołały mi różnicę między funkcjami a symbolami (aka vars), które wskazują na funkcje w Clojure. W programowaniu funkcyjnym funkcje są pierwszej kategorii, więc można je przypisywać, akceptować na wejściu do funkcji i zwracać jako wyjście. Dokładnie tak, jak ma to miejsce w Javie z obiektami, co pozwala nazwać Javę obiektowym językiem.

Również w Thread: Code in book as transcript in REPL pojawiła się ciekawa cecha Scala REPL:
scala> scala> println("Hello, World!")

// Detected repl transcript paste: ctrl-D to finish.

Hello, World!
// Replaying 1 commands from transcript.

scala> println("Hello, World!")
Hello, World!
Wystarczy wkleić dowolną kombinację powyższych linii i REPL wykryje, że wkleiliśmy kawałek kodu, który był prezentowany jako wynik działania REPL. Użyteczna cecha scalowego REPLa. Trochę pokręcone przy próbie wytłumaczenia, więc zachęcam do samodzielnego popróbowania się z tym.

Jeszcze trafiłem na słówko lazy, które jak łatwo było się domyśleć ma służyć opóźnieniu wykonania obliczenia.
scala> lazy val a = 4 + 4
a: Int = <lazy>

scala> a
res13: Int = 8
Nie spędziłem specjalnie wiele czasu na poszukiwania i poprzestanę na poniższym, aby uzmysłowić leniwe obliczenie.
scala> lazy val b = { println("Ala ma kota"); }
b: Unit = <lazy>

scala> b
Ala ma kota
Gdyby usunąć lazy wykonanie przypisania będzie wykonane natychmiast i wyświetli się napis "Ala ma kota", tak jak wykonanie b z lazy powyżej.

Na zakończenie, czytając player/winner typo, zwróciłem uwagę na
if (p1.score > p2.score) declareWinner(p1) else declareWinner(p2)
które powtarza wywołanie funkcji declareWinner. Sądzę, że możnaby zamienić to na reduce. W Clojure już mam, pracuję nad wersją w Scali. Może ktoś pomoże?

Poniżej kod w Clojure. Uzmysławia stosowanie mapy jako jednej z podstawowych struktur danych (coś ala javowy obiekt) oraz map i reduce - filary programowania funkcyjnego w Clojure.
$ lein2 repl
nREPL server started on port 55072
Welcome to REPL-y!
Clojure 1.3.0
    Exit: Control+D or (exit) or (quit)
Commands: (help)
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
          (sourcery function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
Examples from clojuredocs.org:
          (clojuredocs name-here)
          (clojuredocs "ns-here" "name-here")
nil
user=> (def players {:p1 10 :p2 20 :p3 15 :p4 8 :p5 2})
#'user/players
user=> (defn pme [me] [(me 0) (me 1)])
#'user/pme
user=> (map pme players)
([:p1 10] [:p3 15] [:p2 20] [:p4 8] [:p5 2])
user=> (defn >me [me1 me2]
         (if (> (val me2) (val me1)) me2 me1))
#'user/>me
user=> (reduce >me players)
[:p2 20]
user=> (defn declareWinner [[name score]]
         (format "Player %s won (score: %s)" name score))
#'user/declareWinner
user=> (declareWinner
         (reduce >me players))
"Player :p2 won (score: 20)"
Proszę pytać, jak coś niejasne. Chętnie udzielę odpowiedzi.

2 komentarze:

  1. Śledzę sobie Twoje zmagania ze Scalą.
    Jeśli chodzi o drugą definicję add, to zwracam uwagę, że to nie jest funkcja tylko metoda. To nie to samo. Funkcje są wartościami, a metody nie są.
    Ponadto może Cię zainteresują inne jeszcze definicje:
    val add = (x:Int,y:Int) => x + y
    val add:(Int, Int) => Int = _ + _
    val add = (_:Int) + (_:Int)
    object add { def apply(x:Int, y:Int) = x+y }
    val add = new Function2[Int,Int,Int] { def apply(x:Int, y:Int) = x+y }
    Grzegorz

    OdpowiedzUsuń
  2. Jeszcze reduce:
    val players = Map( 'p1 -> 10, 'p2 -> 20, 'p3 -> 15, 'p4 -> 8, 'p5 -> 2 )
    players.reduce{(a,b) => if(a._2 > b._2) a else b}
    Grzegorz

    OdpowiedzUsuń