SlideShare une entreprise Scribd logo
1  sur  90
Main sponsor




(map Clojure everyday-tasks)
        Jacek Laskowski
About me
•   Functional apprentice to Clojure

•   Founder and co-leader of Warszawa JUG

•   Conference organizer of Javarsovia, Confitura, warsjawa

•   Member of NetBeans DreamTeam

•   Blogger of http://JacekLaskowski.pl

•   Blogger of http://blog.japila.pl

•   @jaceklaskowski

•   Member of Apache Software Foundation

•   IBMer
Why should I care
   about Clojure?
         (map Clojure everyday-tasks)
Clojure for real-world, day-to-day programming
List comprehension

(for [x (range 2)
      y (range 2)]
  [x y])
List comprehension

(for [x (range 2)
      y (range 2)]
  [x y])
user=> ([0 0] [0 1] [1 0] [1 1])
List comprehension
function

(for [x (range 2)
      y (range 2)]
  [x y])
user=> ([0 0] [0 1] [1 0] [1 1])
Persistent data
         structures
• list ()
• vector []
• map {}
• set #{}
• They can nest freely
• They’re persistent (a.k.a. immutable)
(for [n #{"jacek" "agatka"
          "iwetka" "patryk"
          "maksym"}]
  [n (count n)])
Lists in Clojure
1st item


(fn arg1 arg2 ...)
                 3rd item
      2nd item
Function calls in
       Clojure
function name


   (fn arg1 arg2 ...)
                function param
      function param
Reason #1
Functional programming
        language
Functional language
• Functions are first-class citizens
• They’re like other values
• They can be
 • passed to a function
 • returned from a function
• HOF = higher-order function
Function definition
  (fn [args]
    (body)

  (defn function-name [args]
   (body))

  #(body)
Function call


(function-name arg1 arg2 ...)
Fundamental functions
• map - apply a function to a sequence
 • returns a sequence
• reduce - accumulation over a sequence
 • returns an accumulator
• filter - filters items satisfying a predicate
 • return a sequence
map function
                                                            Apply a function
      (map f ‘(1 2 3))                                       to a sequence
                                                        and return a sequence
                                                        in which every item is
     ‘((f 1) (f 2) (f 3))                                 transformed by the
                                                                function


Examples: addVat, convertCurrency
http://cyrille.martraire.com/2011/03/thinking-functional-programming-with-map-and-fold-in-your-everyday-java/
reduce function

(reduce f ‘( 1 2 3 4 ))            Accumulate
                                over a sequence
                                       and
‘( f ( f ( f 1 2 ) 3 ) 4 )   return the accumulator
Mutation
• Persistent references to a mutable state
 • Var - dynamically rebound on a per-
    thread basis (thread isolation)
  • Ref - transactional via Clojure STM
  • Agent - independent, asynchronous
    change of individual location
  • Atom - synchronous, independent state
(def ^:dynamic *b* 5)

(defn printb []
 *b*)

(binding [*b* 10]
 (printb))
(def ^:dynamic *b* 5)

(defn printb []
 *b*)

(binding [*b* 10]
 (printb))
user=> 10
explicit about mutation

(def ^:dynamic *b* 5)

(defn printb []
 *b*)

(binding [*b* 10]
 (printb))
user=> 10
(def a (ref 0))
(def b (ref 0))

(alter a inc) ;; java.lang.IllegalStateException:
              ;; No transaction running
(dosync
  (alter a inc)
  (alter b inc))
(def a (ref 0))
(def b (ref 0))

(alter a inc) ;; java.lang.IllegalStateException:
              ;; No transaction running
(dosync
  (alter a inc)
  (alter b inc))
user=> 1
explicit about mutation

  (def a (ref 0))
  (def b (ref 0))

  (alter a inc) ;; java.lang.IllegalStateException:
                ;; No transaction running
  (dosync
    (alter a inc)
    (alter b inc))
  user=> 1
(def c (agent 0))

(send c inc)
(def c (agent 0))

(send c inc)
user=> 1
explicit about mutation

 (def c (agent 0))

 (send c inc)
  user=> 1
(def c (agent 0))

(defn f-agt [v]
 (Thread/sleep (* v 1000))
 (println (Thread/currentThread))
 (inc v))

(send c f-agt)

;; (agent-errors c)
;; (restart-agent c 0)
(def atm (atom 0))

(swap! atm inc)
(def atm (atom 0))

(swap! atm inc)
user=> 1
explicit about mutation

(def atm (atom 0))

(swap! atm inc)
user=> 1
Reason #2
Concurrency abstractions
Namespace, Symbols
     and Vars
• Symbol is a name bound to a Var
• (def v ...)
• Vars can be changed on a per-thread
  basis
• Namespace is a map of symbols to Vars
• Namespace is similar to a fully-qualified
  class name in Java
Clojure REPL
(defn handle-numbers [handler]
 (fn []
   (doseq [x (iterate inc 0)]
      (println (handler x)
      (Thread/sleep 1000)))))

(defn sample-handler [x] x)

(def th (Thread. (handle-numbers sample-handler)))

(.start th)

(defn sample-handler [x] (- x))

(.stop th)

(defn sample-handler [x] x)

(def th (Thread. (handle-numbers #'sample-handler)))

(.start th)
(import [javax.swing JFrame]
        [java.awt.event ActionListener])

(def f (JFrame. "A window"))
(.setSize f 300 100)
(.setVisible f true)

(import [javax.swing JButton])
(def b (JButton. "Press me!"))
(.add f b)
(.pack f)

(.addActionListener b
  (proxy [ActionListener] []
   (actionPerformed [evt]
     (println "I’ve been pressed"))))
Reason #3
Dynamic programming
Values in Clojure

• Strings are just instances of java.lang.String
  user=> (.charAt “abc” 0)
• Numbers, characters, nil, true, false, and
  keywords evaluate to themselves
  user=> (Character/isLetter c)
Regular expressions
• #"pattern" - java.util.regex.Pattern
• (re-seq re s) lazy seq of matches of re in s
• (re-find m) the next regex match
• (re-matches re s) returns the match
• (re-matcher re s) gives j.u.regex.Matcher
• (re-groups m) returns the groups
user=> (def ptrn #"[0-9]+")
#'user/ptrn
user=> (class ptrn)
java.util.regex.Pattern
user=> (re-seq ptrn "abc 123 a 2 2 1")
("123" "2" "2" "1")
Java interop

• (.instanceMember instance args*)
• (.instanceMember Classname args*)
• (Classname/staticMethod args*)
• Classname/staticField
Java interop macros

• (.. instance-expr member+)
• (doto instance-expr
     (instanceMethodName-symbol args*)*)
• (Classname. args*)
Reason #4
Easy Java interop
clojure.main


• Run scripts from the command line
• java -jar clojure.jar your-script.clj args*
AOT compilation
• Clojure compiles all code you load on-the-
  fly into JVM bytecode
• Ahead-of-time (AOT) = before on-the-fly
  at runtime
• Target of (compile) is namespace
• Each file, fn, and gen-class give new .class
:gen-class sample
    (ns helloworld   Classname


 
:gen-class sample
    (ns helloworld
      (:gen-class))   Execute AOT
 
:gen-class sample
    (ns helloworld
      (:gen-class))
 
    (defn -main [& args]   the main function
:gen-class sample
    (ns helloworld
      (:gen-class))
 
    (defn -main [& args]
      (println "Hello world!"))   the main’s body
:gen-class sample
    (ns helloworld
      (:gen-class))
 
    (defn -main [& args]
      (println "Hello world!"))
AOT in Practice

• lein new [project-name]
• Add :main to project.clj
• lein uberjar
• java -jar [project-name-ver-standalone.jar]
Java EE web apps with Clojure
(Maven and Leiningen are there, too!)
 http://blog.japila.pl/2012/03/java-ee-web-apps-with-clojure-maven-and-leiningen-are-there-too/
$ mvn archetype:generate 
  -DarchetypeArtifactId=maven-archetype-webapp 
  -DgroupId=com.mycompany.app 
  -DartifactId=my-webapp-demo
$ mvn archetype:generate 
  -DarchetypeArtifactId=maven-archetype-webapp 
  -DgroupId=com.mycompany.app 
  -DartifactId=my-webapp-demo
$ lein new my-webapp-demo-clj
$ mvn archetype:generate 
  -DarchetypeArtifactId=maven-archetype-webapp 
  -DgroupId=com.mycompany.app 
  -DartifactId=my-webapp-demo
$ lein new my-webapp-demo-clj
(ns my-webapp-demo-clj.core
  (:gen-class
   :methods [[hello [String] String]]))

(defn -hello
  [this s]
  (str "Hello, " s))
$ mvn archetype:generate 
  -DarchetypeArtifactId=maven-archetype-webapp 
  -DgroupId=com.mycompany.app 
  -DartifactId=my-webapp-demo
$ lein new my-webapp-demo-clj
                                    public String hello(String)
(ns my-webapp-demo-clj.core
  (:gen-class
   :methods [[hello [String] String]]))

(defn -hello
  [this s]
  (str "Hello, " s))
(defproject my-webapp-demo-clj "1.0.0-SNAPSHOT"
  :description "FIXME: write description"
  :dependencies [[org.clojure/clojure "1.3.0"]]
  :aot :all)
(defproject my-webapp-demo-clj "1.0.0-SNAPSHOT"
  :description "FIXME: write description"
  :dependencies [[org.clojure/clojure "1.3.0"]]
  :aot :all)
$ lein install
(defproject my-webapp-demo-clj "1.0.0-SNAPSHOT"
  :description "FIXME: write description"
  :dependencies [[org.clojure/clojure "1.3.0"]]
  :aot :all)
$ lein install
<dependency>
  <groupId>my-webapp-demo-clj</groupId>
  <artifactId>my-webapp-demo-clj</artifactId>
  <version>1.0.0-SNAPSHOT</version>
</dependency>
<html>
<body>
<h2>Hello World!</h2>
</body>
<p>
 <%= new my_webapp_demo_clj.core().hello("Jacek") %>
</p>
</html>
<html>
<body>
<h2>Hello World!</h2>
</body>
<p>
 <%= new my_webapp_demo_clj.core().hello("Jacek") %>
</p>
</html>
$ mvn package
<html>
<body>
<h2>Hello World!</h2>
</body>
<p>
 <%= new my_webapp_demo_clj.core().hello("Jacek") %>
</p>
</html>
$ mvn package
Reason #5
Easy Java interop both
         ways
Leiningen
lein - a project automation tool
Starting a project

• lein new [project-name]
 • Edit project.clj (mainly deps)
 • Also plugin configuration, e.g. :main for
    run (soon explained)
Custom project setup
   defproject in project.clj customized
Dependencies


• lein deps
• lein search [library]
clojars
Maven repository for Clojure projects
         http://clojars.org/
Ongoing development
• lein repl
• lein eclipse
• lein midje (“formerly” lein test)
• lein run
• lein jar
• Some require custom setup...
Unit testing
• clojure.test namespace
• the is macro
     (is (= 5 (+ 2 2)) "Crazy arithmetic")
• the are macro
• the deftest macro
     (deftest addition
       (is (= 4 (+ 2 2)))
       (is (= 7 (+ 3 4))))
• (run-tests & namespaces)
lein test [ns]
Runs project tests, optionally only from a ns
src/hi/core.clj
(ns hi.core)

(defn hi
 ([] "Hi!")
 ([name] (str "Hi " name "!")))
lein test
$ lein test
Copying 1 file to /Users/jacek/sandbox/hi/lib

Testing hi.test.core

FAIL in (replace-me) (core.clj:6)
No tests have been written.
expected: false
 actual: false

Ran 1 tests containing 1 assertions.
1 failures, 0 errors.
test/hi/test/core.clj
(ns hi.test.core
 (:use [hi.core])
 (:use [clojure.test]))

(deftest hi-simple-test
 (is (= (hi) "Hi!"))
 (is (= (hi "Clojure") "Hi Clojure!")))
lein test
$ lein test

Testing hi.test.core

Ran 1 tests containing 2 assertions.
0 failures, 0 errors.
Modern unit testing
• Midje - a TDD library for Clojure that
  supports top-down ('mockish') TDD
• https://github.com/marick/Midje
• midje.sweet namespace
• fact macro
• (fact "one plus one is two"
   (+ 1 1) => 2)
Midje and Leiningen

• lein plugin install lein-midje 1.0.8
• :dev-dependencies [[lein-midje "1.0.8"]
                          [midje "1.3.1"]]
• lein midje
• lein midje --lazytest
TDD with Midje #0

• :repositories
  ["stuart" "http://stuartsierra.com/maven2"]
• :dev-dependencies
  [com.stuartsierra/lazytest "1.2.3"]
TDD with Midje #1
$ lein midje --lazytest
======================================
At #<Date Tue Feb 21 13:07:52 CET 2012>

Reloading librarian-clojure.run, librarian-clojure.repl, librarian-clojure.test.core, librarian-clojure.core,
librarian-clojure.books, librarian-clojure.db

FAIL at (core.clj:10)
  Expected: 2
   Actual: 3

FAIL at (core.clj:10)
  Expected: 2
   Actual: 3

FAILURE: 1 fact was not confirmed. (But 1 was.)

Done.

======================================
At #<Date Tue Feb 21 13:08:11 CET 2012>

Reloading librarian-clojure.test.core
All claimed facts (2) have been confirmed.

Done.
TDD with Midje #2

• (fact "doubles odd numbers"
    (my-func 3) => 6)
• (fact "triples even numbers"
    (my-func 4) => 12)
• A solution?

                         http://www.lispplusplus.com/2012/02/tdd-in-midje-in-nutshell.html
TDD with Midje #3
• Which solution do you prefer?
• (defn my-func [n]
    (if (odd? n)
      (* 2 n)
      (* 3 n))
• (defn my-func [n]
    (let [multiplier (if (odd? n) 2 3)]
      (* multiplier n))
• Monitor lein terminal
Reason #6
Familiar-looking project
          tools
Ring
Clojure HTTP server applications
https://github.com/mmcgrana/ring
Compojure
  A concise web framework for Clojure
https://github.com/weavejester/compojure
Ring up

jacek:~/oss/librarian-clojure
$ lein ring server


:ring {:handler librarian-clojure.core/app}
Reason #7
Web application libraries
Multimethods
• Runtime dynamic dispatch
• (defmulti say-count count)
  (defmethod say-count 0 [_] "Zero")
  (defmethod say-count 1 [_] "One")
  (defmethod say-count :default [_]
          "A Bunch")
• (say-count [1 2 3])
          http://blog.fogus.me/2011/10/14/why-clojure-doesnt-need-invokedynamic-but-it-might-be-nice/
user=> (defmulti say-class class)

user=> (defmethod say-class (class {}) [_] "A map")
user=> (say-class {})
"A map"
user=> (say-class [])
java.lang.IllegalArgumentException: No method in multimethod
'say-class' for dispatch value: class clojure.lang.PersistentVector
(NO_SOURCE_FILE:0)

user=> (defmethod say-class (class []) [_] "A vector")
user=> (say-class [])
"A vector"
Reason #8
Multi-level dispatch
Are you still uncertain?

 There’s more, but...
time flies by so fast :(

Contenu connexe

Tendances

Clojure for Java developers - Stockholm
Clojure for Java developers - StockholmClojure for Java developers - Stockholm
Clojure for Java developers - Stockholm
Jan Kronquist
 
Java 7, 8 & 9 - Moving the language forward
Java 7, 8 & 9 - Moving the language forwardJava 7, 8 & 9 - Moving the language forward
Java 7, 8 & 9 - Moving the language forward
Mario Fusco
 
Jggug 2010 330 Grails 1.3 観察
Jggug 2010 330 Grails 1.3 観察Jggug 2010 330 Grails 1.3 観察
Jggug 2010 330 Grails 1.3 観察
Tsuyoshi Yamamoto
 

Tendances (20)

Scala coated JVM
Scala coated JVMScala coated JVM
Scala coated JVM
 
The Ring programming language version 1.6 book - Part 15 of 189
The Ring programming language version 1.6 book - Part 15 of 189The Ring programming language version 1.6 book - Part 15 of 189
The Ring programming language version 1.6 book - Part 15 of 189
 
From Java to Parellel Clojure - Clojure South 2019
From Java to Parellel Clojure - Clojure South 2019From Java to Parellel Clojure - Clojure South 2019
From Java to Parellel Clojure - Clojure South 2019
 
What can be done with Java, but should better be done with Erlang (@pavlobaron)
What can be done with Java, but should better be done with Erlang (@pavlobaron)What can be done with Java, but should better be done with Erlang (@pavlobaron)
What can be done with Java, but should better be done with Erlang (@pavlobaron)
 
Sneaking inside Kotlin features
Sneaking inside Kotlin featuresSneaking inside Kotlin features
Sneaking inside Kotlin features
 
T3chFest 2016 - The polyglot programmer
T3chFest 2016 - The polyglot programmerT3chFest 2016 - The polyglot programmer
T3chFest 2016 - The polyglot programmer
 
The Ring programming language version 1.6 book - Part 36 of 189
The Ring programming language version 1.6 book - Part 36 of 189The Ring programming language version 1.6 book - Part 36 of 189
The Ring programming language version 1.6 book - Part 36 of 189
 
The Ring programming language version 1.7 book - Part 16 of 196
The Ring programming language version 1.7 book - Part 16 of 196The Ring programming language version 1.7 book - Part 16 of 196
The Ring programming language version 1.7 book - Part 16 of 196
 
Clojure 1.1 And Beyond
Clojure 1.1 And BeyondClojure 1.1 And Beyond
Clojure 1.1 And Beyond
 
ClojureScript loves React, DomCode May 26 2015
ClojureScript loves React, DomCode May 26 2015ClojureScript loves React, DomCode May 26 2015
ClojureScript loves React, DomCode May 26 2015
 
Clojure for Java developers - Stockholm
Clojure for Java developers - StockholmClojure for Java developers - Stockholm
Clojure for Java developers - Stockholm
 
Java 8 Workshop
Java 8 WorkshopJava 8 Workshop
Java 8 Workshop
 
Java 7, 8 & 9 - Moving the language forward
Java 7, 8 & 9 - Moving the language forwardJava 7, 8 & 9 - Moving the language forward
Java 7, 8 & 9 - Moving the language forward
 
Programmation fonctionnelle en JavaScript
Programmation fonctionnelle en JavaScriptProgrammation fonctionnelle en JavaScript
Programmation fonctionnelle en JavaScript
 
ES6 - Next Generation Javascript
ES6 - Next Generation JavascriptES6 - Next Generation Javascript
ES6 - Next Generation Javascript
 
G*ワークショップ in 仙台 Grails(とことん)入門
G*ワークショップ in 仙台 Grails(とことん)入門G*ワークショップ in 仙台 Grails(とことん)入門
G*ワークショップ in 仙台 Grails(とことん)入門
 
Jggug 2010 330 Grails 1.3 観察
Jggug 2010 330 Grails 1.3 観察Jggug 2010 330 Grails 1.3 観察
Jggug 2010 330 Grails 1.3 観察
 
The Ring programming language version 1.8 book - Part 86 of 202
The Ring programming language version 1.8 book - Part 86 of 202The Ring programming language version 1.8 book - Part 86 of 202
The Ring programming language version 1.8 book - Part 86 of 202
 
GUL UC3M - Introduction to functional programming
GUL UC3M - Introduction to functional programmingGUL UC3M - Introduction to functional programming
GUL UC3M - Introduction to functional programming
 
ES2015 (ES6) Overview
ES2015 (ES6) OverviewES2015 (ES6) Overview
ES2015 (ES6) Overview
 

En vedette

Macros in Clojure
Macros in ClojureMacros in Clojure
Macros in Clojure
sohta
 
Clojureシンタックスハイライター開発から考えるこれからのlispに必要なもの
Clojureシンタックスハイライター開発から考えるこれからのlispに必要なものClojureシンタックスハイライター開発から考えるこれからのlispに必要なもの
Clojureシンタックスハイライター開発から考えるこれからのlispに必要なもの
sohta
 
Messaging With Erlang And Jabber
Messaging With  Erlang And  JabberMessaging With  Erlang And  Jabber
Messaging With Erlang And Jabber
l xf
 

En vedette (20)

Clojure: a LISP for the JVM
Clojure: a LISP for the JVMClojure: a LISP for the JVM
Clojure: a LISP for the JVM
 
Introduction to clojure
Introduction to clojureIntroduction to clojure
Introduction to clojure
 
DSL in Clojure
DSL in ClojureDSL in Clojure
DSL in Clojure
 
A little exercise with clojure macro
A little exercise with clojure macroA little exercise with clojure macro
A little exercise with clojure macro
 
Clojure: Practical functional approach on JVM
Clojure: Practical functional approach on JVMClojure: Practical functional approach on JVM
Clojure: Practical functional approach on JVM
 
Clojure Macros Workshop: LambdaJam 2013 / CUFP 2013
Clojure Macros Workshop: LambdaJam 2013 / CUFP 2013Clojure Macros Workshop: LambdaJam 2013 / CUFP 2013
Clojure Macros Workshop: LambdaJam 2013 / CUFP 2013
 
A Dive Into Clojure
A Dive Into ClojureA Dive Into Clojure
A Dive Into Clojure
 
不自然なcar/ナチュラルにconsして
不自然なcar/ナチュラルにconsして不自然なcar/ナチュラルにconsして
不自然なcar/ナチュラルにconsして
 
Writing Macros
Writing MacrosWriting Macros
Writing Macros
 
Patterns
PatternsPatterns
Patterns
 
Macros in Clojure
Macros in ClojureMacros in Clojure
Macros in Clojure
 
入門ClojureScript
入門ClojureScript入門ClojureScript
入門ClojureScript
 
Continuation Passing Style and Macros in Clojure - Jan 2012
Continuation Passing Style and Macros in Clojure - Jan 2012Continuation Passing Style and Macros in Clojure - Jan 2012
Continuation Passing Style and Macros in Clojure - Jan 2012
 
Clojure的魅力
Clojure的魅力Clojure的魅力
Clojure的魅力
 
Clojure概览
Clojure概览Clojure概览
Clojure概览
 
Stefan Richter - Writing simple, readable and robust code: Examples in Java, ...
Stefan Richter - Writing simple, readable and robust code: Examples in Java, ...Stefan Richter - Writing simple, readable and robust code: Examples in Java, ...
Stefan Richter - Writing simple, readable and robust code: Examples in Java, ...
 
Clojureシンタックスハイライター開発から考えるこれからのlispに必要なもの
Clojureシンタックスハイライター開発から考えるこれからのlispに必要なものClojureシンタックスハイライター開発から考えるこれからのlispに必要なもの
Clojureシンタックスハイライター開発から考えるこれからのlispに必要なもの
 
Winning the Erlang Edit•Build•Test Cycle
Winning the Erlang Edit•Build•Test CycleWinning the Erlang Edit•Build•Test Cycle
Winning the Erlang Edit•Build•Test Cycle
 
Messaging With Erlang And Jabber
Messaging With  Erlang And  JabberMessaging With  Erlang And  Jabber
Messaging With Erlang And Jabber
 
Elixir talk
Elixir talkElixir talk
Elixir talk
 

Similaire à (map Clojure everyday-tasks)

Clojure - A new Lisp
Clojure - A new LispClojure - A new Lisp
Clojure - A new Lisp
elliando dias
 
Clojure and Modularity
Clojure and ModularityClojure and Modularity
Clojure and Modularity
elliando dias
 
Predictably
PredictablyPredictably
Predictably
ztellman
 
JavaScript Growing Up
JavaScript Growing UpJavaScript Growing Up
JavaScript Growing Up
David Padbury
 

Similaire à (map Clojure everyday-tasks) (20)

Pune Clojure Course Outline
Pune Clojure Course OutlinePune Clojure Course Outline
Pune Clojure Course Outline
 
Clojure - A new Lisp
Clojure - A new LispClojure - A new Lisp
Clojure - A new Lisp
 
Clojure Intro
Clojure IntroClojure Intro
Clojure Intro
 
Clojure And Swing
Clojure And SwingClojure And Swing
Clojure And Swing
 
Exploring Clojurescript
Exploring ClojurescriptExploring Clojurescript
Exploring Clojurescript
 
Lisp Macros in 20 Minutes (Featuring Clojure)
Lisp Macros in 20 Minutes (Featuring Clojure)Lisp Macros in 20 Minutes (Featuring Clojure)
Lisp Macros in 20 Minutes (Featuring Clojure)
 
Clojure made-simple - John Stevenson
Clojure made-simple - John StevensonClojure made-simple - John Stevenson
Clojure made-simple - John Stevenson
 
Clojure and Modularity
Clojure and ModularityClojure and Modularity
Clojure and Modularity
 
Php 5.4: New Language Features You Will Find Useful
Php 5.4: New Language Features You Will Find UsefulPhp 5.4: New Language Features You Will Find Useful
Php 5.4: New Language Features You Will Find Useful
 
Getting started with Clojure
Getting started with ClojureGetting started with Clojure
Getting started with Clojure
 
Clojure made simple - Lightning talk
Clojure made simple - Lightning talkClojure made simple - Lightning talk
Clojure made simple - Lightning talk
 
Clojure intro
Clojure introClojure intro
Clojure intro
 
ClojureScript for the web
ClojureScript for the webClojureScript for the web
ClojureScript for the web
 
DevOps with Fabric
DevOps with FabricDevOps with Fabric
DevOps with Fabric
 
Functional programming with clojure
Functional programming with clojureFunctional programming with clojure
Functional programming with clojure
 
Predictably
PredictablyPredictably
Predictably
 
Clojure concurrency overview
Clojure concurrency overviewClojure concurrency overview
Clojure concurrency overview
 
JavaScript Growing Up
JavaScript Growing UpJavaScript Growing Up
JavaScript Growing Up
 
Funkcija, objekt, python
Funkcija, objekt, pythonFunkcija, objekt, python
Funkcija, objekt, python
 
Clojure+ClojureScript Webapps
Clojure+ClojureScript WebappsClojure+ClojureScript Webapps
Clojure+ClojureScript Webapps
 

Plus de Jacek Laskowski

Developing modular applications with Java EE 6 and Enterprise OSGi + WebSpher...
Developing modular applications with Java EE 6 and Enterprise OSGi + WebSpher...Developing modular applications with Java EE 6 and Enterprise OSGi + WebSpher...
Developing modular applications with Java EE 6 and Enterprise OSGi + WebSpher...
Jacek Laskowski
 
Apache Tomcat + Java EE = Apache TomEE
Apache Tomcat + Java EE = Apache TomEEApache Tomcat + Java EE = Apache TomEE
Apache Tomcat + Java EE = Apache TomEE
Jacek Laskowski
 

Plus de Jacek Laskowski (11)

 Kafka Streams VS Spark Structured Streaming - Modern Stream Processing Engin...
 Kafka Streams VS Spark Structured Streaming - Modern Stream Processing Engin... Kafka Streams VS Spark Structured Streaming - Modern Stream Processing Engin...
 Kafka Streams VS Spark Structured Streaming - Modern Stream Processing Engin...
 
Opening slides to Warsaw Scala FortyFives on Testing tools
Opening slides to Warsaw Scala FortyFives on Testing toolsOpening slides to Warsaw Scala FortyFives on Testing tools
Opening slides to Warsaw Scala FortyFives on Testing tools
 
#Be #social #FTW aka Your #Professional #Development with #StackOverflow #Git...
#Be #social #FTW aka Your #Professional #Development with #StackOverflow #Git...#Be #social #FTW aka Your #Professional #Development with #StackOverflow #Git...
#Be #social #FTW aka Your #Professional #Development with #StackOverflow #Git...
 
StackOverflow, GitHub, twitter, reddit i Twój profesjonalny rozwój
StackOverflow, GitHub, twitter, reddit i Twój profesjonalny rozwójStackOverflow, GitHub, twitter, reddit i Twój profesjonalny rozwój
StackOverflow, GitHub, twitter, reddit i Twój profesjonalny rozwój
 
Introduction to Web Application Development in Clojure
Introduction to Web Application Development in ClojureIntroduction to Web Application Development in Clojure
Introduction to Web Application Development in Clojure
 
Introduction to Functional Programming in Scala
Introduction to Functional Programming in ScalaIntroduction to Functional Programming in Scala
Introduction to Functional Programming in Scala
 
Moje pierwsze kroki w programowaniu funkcyjnym w Scali
Moje pierwsze kroki w programowaniu funkcyjnym w ScaliMoje pierwsze kroki w programowaniu funkcyjnym w Scali
Moje pierwsze kroki w programowaniu funkcyjnym w Scali
 
Functional web development with Git(Hub), Heroku and Clojure
Functional web development with Git(Hub), Heroku and ClojureFunctional web development with Git(Hub), Heroku and Clojure
Functional web development with Git(Hub), Heroku and Clojure
 
Praktyczne wprowadzenie do OSGi i Enterprise OSGi
Praktyczne wprowadzenie do OSGi i Enterprise OSGiPraktyczne wprowadzenie do OSGi i Enterprise OSGi
Praktyczne wprowadzenie do OSGi i Enterprise OSGi
 
Developing modular applications with Java EE 6 and Enterprise OSGi + WebSpher...
Developing modular applications with Java EE 6 and Enterprise OSGi + WebSpher...Developing modular applications with Java EE 6 and Enterprise OSGi + WebSpher...
Developing modular applications with Java EE 6 and Enterprise OSGi + WebSpher...
 
Apache Tomcat + Java EE = Apache TomEE
Apache Tomcat + Java EE = Apache TomEEApache Tomcat + Java EE = Apache TomEE
Apache Tomcat + Java EE = Apache TomEE
 

Dernier

Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
vu2urc
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
Joaquim Jorge
 

Dernier (20)

Histor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slideHistor y of HAM Radio presentation slide
Histor y of HAM Radio presentation slide
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
GenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdfGenAI Risks & Security Meetup 01052024.pdf
GenAI Risks & Security Meetup 01052024.pdf
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Advantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your BusinessAdvantages of Hiring UIUX Design Service Providers for Your Business
Advantages of Hiring UIUX Design Service Providers for Your Business
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
AWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of TerraformAWS Community Day CPH - Three problems of Terraform
AWS Community Day CPH - Three problems of Terraform
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
HTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation StrategiesHTML Injection Attacks: Impact and Mitigation Strategies
HTML Injection Attacks: Impact and Mitigation Strategies
 

(map Clojure everyday-tasks)

  • 1. Main sponsor (map Clojure everyday-tasks) Jacek Laskowski
  • 2. About me • Functional apprentice to Clojure • Founder and co-leader of Warszawa JUG • Conference organizer of Javarsovia, Confitura, warsjawa • Member of NetBeans DreamTeam • Blogger of http://JacekLaskowski.pl • Blogger of http://blog.japila.pl • @jaceklaskowski • Member of Apache Software Foundation • IBMer
  • 3. Why should I care about Clojure? (map Clojure everyday-tasks) Clojure for real-world, day-to-day programming
  • 4. List comprehension (for [x (range 2) y (range 2)] [x y])
  • 5. List comprehension (for [x (range 2) y (range 2)] [x y]) user=> ([0 0] [0 1] [1 0] [1 1])
  • 6. List comprehension function (for [x (range 2) y (range 2)] [x y]) user=> ([0 0] [0 1] [1 0] [1 1])
  • 7. Persistent data structures • list () • vector [] • map {} • set #{} • They can nest freely • They’re persistent (a.k.a. immutable)
  • 8. (for [n #{"jacek" "agatka" "iwetka" "patryk" "maksym"}] [n (count n)])
  • 9. Lists in Clojure 1st item (fn arg1 arg2 ...) 3rd item 2nd item
  • 10. Function calls in Clojure function name (fn arg1 arg2 ...) function param function param
  • 12. Functional language • Functions are first-class citizens • They’re like other values • They can be • passed to a function • returned from a function • HOF = higher-order function
  • 13. Function definition (fn [args] (body) (defn function-name [args] (body)) #(body)
  • 15. Fundamental functions • map - apply a function to a sequence • returns a sequence • reduce - accumulation over a sequence • returns an accumulator • filter - filters items satisfying a predicate • return a sequence
  • 16. map function Apply a function (map f ‘(1 2 3)) to a sequence and return a sequence in which every item is ‘((f 1) (f 2) (f 3)) transformed by the function Examples: addVat, convertCurrency http://cyrille.martraire.com/2011/03/thinking-functional-programming-with-map-and-fold-in-your-everyday-java/
  • 17. reduce function (reduce f ‘( 1 2 3 4 )) Accumulate over a sequence and ‘( f ( f ( f 1 2 ) 3 ) 4 ) return the accumulator
  • 18. Mutation • Persistent references to a mutable state • Var - dynamically rebound on a per- thread basis (thread isolation) • Ref - transactional via Clojure STM • Agent - independent, asynchronous change of individual location • Atom - synchronous, independent state
  • 19. (def ^:dynamic *b* 5) (defn printb [] *b*) (binding [*b* 10] (printb))
  • 20. (def ^:dynamic *b* 5) (defn printb [] *b*) (binding [*b* 10] (printb)) user=> 10
  • 21. explicit about mutation (def ^:dynamic *b* 5) (defn printb [] *b*) (binding [*b* 10] (printb)) user=> 10
  • 22. (def a (ref 0)) (def b (ref 0)) (alter a inc) ;; java.lang.IllegalStateException: ;; No transaction running (dosync (alter a inc) (alter b inc))
  • 23. (def a (ref 0)) (def b (ref 0)) (alter a inc) ;; java.lang.IllegalStateException: ;; No transaction running (dosync (alter a inc) (alter b inc)) user=> 1
  • 24. explicit about mutation (def a (ref 0)) (def b (ref 0)) (alter a inc) ;; java.lang.IllegalStateException: ;; No transaction running (dosync (alter a inc) (alter b inc)) user=> 1
  • 25. (def c (agent 0)) (send c inc)
  • 26. (def c (agent 0)) (send c inc) user=> 1
  • 27. explicit about mutation (def c (agent 0)) (send c inc) user=> 1
  • 28. (def c (agent 0)) (defn f-agt [v] (Thread/sleep (* v 1000)) (println (Thread/currentThread)) (inc v)) (send c f-agt) ;; (agent-errors c) ;; (restart-agent c 0)
  • 29. (def atm (atom 0)) (swap! atm inc)
  • 30. (def atm (atom 0)) (swap! atm inc) user=> 1
  • 31. explicit about mutation (def atm (atom 0)) (swap! atm inc) user=> 1
  • 33. Namespace, Symbols and Vars • Symbol is a name bound to a Var • (def v ...) • Vars can be changed on a per-thread basis • Namespace is a map of symbols to Vars • Namespace is similar to a fully-qualified class name in Java
  • 35. (defn handle-numbers [handler]  (fn []    (doseq [x (iterate inc 0)]       (println (handler x)       (Thread/sleep 1000))))) (defn sample-handler [x] x) (def th (Thread. (handle-numbers sample-handler))) (.start th) (defn sample-handler [x] (- x)) (.stop th) (defn sample-handler [x] x) (def th (Thread. (handle-numbers #'sample-handler))) (.start th)
  • 36. (import [javax.swing JFrame] [java.awt.event ActionListener]) (def f (JFrame. "A window")) (.setSize f 300 100) (.setVisible f true) (import [javax.swing JButton]) (def b (JButton. "Press me!")) (.add f b) (.pack f) (.addActionListener b (proxy [ActionListener] [] (actionPerformed [evt] (println "I’ve been pressed"))))
  • 38. Values in Clojure • Strings are just instances of java.lang.String user=> (.charAt “abc” 0) • Numbers, characters, nil, true, false, and keywords evaluate to themselves user=> (Character/isLetter c)
  • 39. Regular expressions • #"pattern" - java.util.regex.Pattern • (re-seq re s) lazy seq of matches of re in s • (re-find m) the next regex match • (re-matches re s) returns the match • (re-matcher re s) gives j.u.regex.Matcher • (re-groups m) returns the groups
  • 40. user=> (def ptrn #"[0-9]+") #'user/ptrn user=> (class ptrn) java.util.regex.Pattern user=> (re-seq ptrn "abc 123 a 2 2 1") ("123" "2" "2" "1")
  • 41. Java interop • (.instanceMember instance args*) • (.instanceMember Classname args*) • (Classname/staticMethod args*) • Classname/staticField
  • 42. Java interop macros • (.. instance-expr member+) • (doto instance-expr (instanceMethodName-symbol args*)*) • (Classname. args*)
  • 44. clojure.main • Run scripts from the command line • java -jar clojure.jar your-script.clj args*
  • 45. AOT compilation • Clojure compiles all code you load on-the- fly into JVM bytecode • Ahead-of-time (AOT) = before on-the-fly at runtime • Target of (compile) is namespace • Each file, fn, and gen-class give new .class
  • 46. :gen-class sample (ns helloworld Classname  
  • 47. :gen-class sample (ns helloworld   (:gen-class)) Execute AOT  
  • 48. :gen-class sample (ns helloworld   (:gen-class))   (defn -main [& args] the main function
  • 49. :gen-class sample (ns helloworld   (:gen-class))   (defn -main [& args]   (println "Hello world!")) the main’s body
  • 50. :gen-class sample (ns helloworld   (:gen-class))   (defn -main [& args]   (println "Hello world!"))
  • 51. AOT in Practice • lein new [project-name] • Add :main to project.clj • lein uberjar • java -jar [project-name-ver-standalone.jar]
  • 52. Java EE web apps with Clojure (Maven and Leiningen are there, too!) http://blog.japila.pl/2012/03/java-ee-web-apps-with-clojure-maven-and-leiningen-are-there-too/
  • 53. $ mvn archetype:generate -DarchetypeArtifactId=maven-archetype-webapp -DgroupId=com.mycompany.app -DartifactId=my-webapp-demo
  • 54. $ mvn archetype:generate -DarchetypeArtifactId=maven-archetype-webapp -DgroupId=com.mycompany.app -DartifactId=my-webapp-demo $ lein new my-webapp-demo-clj
  • 55. $ mvn archetype:generate -DarchetypeArtifactId=maven-archetype-webapp -DgroupId=com.mycompany.app -DartifactId=my-webapp-demo $ lein new my-webapp-demo-clj (ns my-webapp-demo-clj.core   (:gen-class    :methods [[hello [String] String]])) (defn -hello   [this s]   (str "Hello, " s))
  • 56. $ mvn archetype:generate -DarchetypeArtifactId=maven-archetype-webapp -DgroupId=com.mycompany.app -DartifactId=my-webapp-demo $ lein new my-webapp-demo-clj public String hello(String) (ns my-webapp-demo-clj.core   (:gen-class    :methods [[hello [String] String]])) (defn -hello   [this s]   (str "Hello, " s))
  • 57. (defproject my-webapp-demo-clj "1.0.0-SNAPSHOT"   :description "FIXME: write description"   :dependencies [[org.clojure/clojure "1.3.0"]]   :aot :all)
  • 58. (defproject my-webapp-demo-clj "1.0.0-SNAPSHOT"   :description "FIXME: write description"   :dependencies [[org.clojure/clojure "1.3.0"]]   :aot :all) $ lein install
  • 59. (defproject my-webapp-demo-clj "1.0.0-SNAPSHOT"   :description "FIXME: write description"   :dependencies [[org.clojure/clojure "1.3.0"]]   :aot :all) $ lein install <dependency>   <groupId>my-webapp-demo-clj</groupId>   <artifactId>my-webapp-demo-clj</artifactId>   <version>1.0.0-SNAPSHOT</version> </dependency>
  • 60. <html> <body> <h2>Hello World!</h2> </body> <p> <%= new my_webapp_demo_clj.core().hello("Jacek") %> </p> </html>
  • 61. <html> <body> <h2>Hello World!</h2> </body> <p> <%= new my_webapp_demo_clj.core().hello("Jacek") %> </p> </html> $ mvn package
  • 62. <html> <body> <h2>Hello World!</h2> </body> <p> <%= new my_webapp_demo_clj.core().hello("Jacek") %> </p> </html> $ mvn package
  • 63. Reason #5 Easy Java interop both ways
  • 64. Leiningen lein - a project automation tool
  • 65. Starting a project • lein new [project-name] • Edit project.clj (mainly deps) • Also plugin configuration, e.g. :main for run (soon explained)
  • 66. Custom project setup defproject in project.clj customized
  • 67. Dependencies • lein deps • lein search [library]
  • 68. clojars Maven repository for Clojure projects http://clojars.org/
  • 69. Ongoing development • lein repl • lein eclipse • lein midje (“formerly” lein test) • lein run • lein jar • Some require custom setup...
  • 70. Unit testing • clojure.test namespace • the is macro (is (= 5 (+ 2 2)) "Crazy arithmetic") • the are macro • the deftest macro (deftest addition (is (= 4 (+ 2 2))) (is (= 7 (+ 3 4)))) • (run-tests & namespaces)
  • 71. lein test [ns] Runs project tests, optionally only from a ns
  • 72. src/hi/core.clj (ns hi.core) (defn hi ([] "Hi!") ([name] (str "Hi " name "!")))
  • 73. lein test $ lein test Copying 1 file to /Users/jacek/sandbox/hi/lib Testing hi.test.core FAIL in (replace-me) (core.clj:6) No tests have been written. expected: false actual: false Ran 1 tests containing 1 assertions. 1 failures, 0 errors.
  • 74. test/hi/test/core.clj (ns hi.test.core (:use [hi.core]) (:use [clojure.test])) (deftest hi-simple-test (is (= (hi) "Hi!")) (is (= (hi "Clojure") "Hi Clojure!")))
  • 75. lein test $ lein test Testing hi.test.core Ran 1 tests containing 2 assertions. 0 failures, 0 errors.
  • 76. Modern unit testing • Midje - a TDD library for Clojure that supports top-down ('mockish') TDD • https://github.com/marick/Midje • midje.sweet namespace • fact macro • (fact "one plus one is two" (+ 1 1) => 2)
  • 77. Midje and Leiningen • lein plugin install lein-midje 1.0.8 • :dev-dependencies [[lein-midje "1.0.8"] [midje "1.3.1"]] • lein midje • lein midje --lazytest
  • 78. TDD with Midje #0 • :repositories ["stuart" "http://stuartsierra.com/maven2"] • :dev-dependencies [com.stuartsierra/lazytest "1.2.3"]
  • 79. TDD with Midje #1 $ lein midje --lazytest ====================================== At #<Date Tue Feb 21 13:07:52 CET 2012> Reloading librarian-clojure.run, librarian-clojure.repl, librarian-clojure.test.core, librarian-clojure.core, librarian-clojure.books, librarian-clojure.db FAIL at (core.clj:10) Expected: 2 Actual: 3 FAIL at (core.clj:10) Expected: 2 Actual: 3 FAILURE: 1 fact was not confirmed. (But 1 was.) Done. ====================================== At #<Date Tue Feb 21 13:08:11 CET 2012> Reloading librarian-clojure.test.core All claimed facts (2) have been confirmed. Done.
  • 80. TDD with Midje #2 • (fact "doubles odd numbers"   (my-func 3) => 6) • (fact "triples even numbers"   (my-func 4) => 12) • A solution? http://www.lispplusplus.com/2012/02/tdd-in-midje-in-nutshell.html
  • 81. TDD with Midje #3 • Which solution do you prefer? • (defn my-func [n]   (if (odd? n)     (* 2 n)     (* 3 n)) • (defn my-func [n]   (let [multiplier (if (odd? n) 2 3)]     (* multiplier n)) • Monitor lein terminal
  • 83. Ring Clojure HTTP server applications https://github.com/mmcgrana/ring
  • 84. Compojure A concise web framework for Clojure https://github.com/weavejester/compojure
  • 85. Ring up jacek:~/oss/librarian-clojure $ lein ring server :ring {:handler librarian-clojure.core/app}
  • 87. Multimethods • Runtime dynamic dispatch • (defmulti say-count count) (defmethod say-count 0 [_] "Zero") (defmethod say-count 1 [_] "One") (defmethod say-count :default [_] "A Bunch") • (say-count [1 2 3]) http://blog.fogus.me/2011/10/14/why-clojure-doesnt-need-invokedynamic-but-it-might-be-nice/
  • 88. user=> (defmulti say-class class) user=> (defmethod say-class (class {}) [_] "A map") user=> (say-class {}) "A map" user=> (say-class []) java.lang.IllegalArgumentException: No method in multimethod 'say-class' for dispatch value: class clojure.lang.PersistentVector (NO_SOURCE_FILE:0) user=> (defmethod say-class (class []) [_] "A vector") user=> (say-class []) "A vector"
  • 90. Are you still uncertain? There’s more, but... time flies by so fast :(

Notes de l'éditeur

  1. \n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n
  44. \n
  45. \n
  46. \n
  47. \n
  48. \n
  49. \n
  50. \n
  51. \n
  52. \n
  53. \n
  54. \n
  55. \n
  56. \n
  57. \n
  58. \n
  59. \n
  60. \n
  61. \n
  62. \n
  63. \n
  64. \n
  65. \n
  66. \n
  67. \n
  68. \n
  69. \n
  70. \n
  71. \n
  72. \n
  73. \n
  74. \n
  75. \n
  76. \n
  77. \n
  78. \n
  79. \n
  80. \n
  81. \n
  82. \n