Clojure #2
Upcoming SlideShare
Loading in...5
×
 

Clojure #2

on

  • 482 views

 

Statistics

Views

Total Views
482
Views on SlideShare
482
Embed Views
0

Actions

Likes
0
Downloads
7
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Clojure #2 Clojure #2 Presentation Transcript

  • Clojure #2
  • Functional collections
  • map Для преобразования всех элементов коллекции можно использовать обычную функцию map: (defn fun [i] (+ 1 i)) (map fun [1 2 3]) View slide
  • mapcat Аналогично flatMap в Scala, в Clojure есть mapcat. (defn fun[i] (repeat i i)) (mapcat fun [1 2 3]) Получится (1 2 2 3 3 3) View slide
  • filter and remove Две по сути одинаковые функции, только отличаются условием предиката (filter even? (1 2 3 4)) ;(2 4) (remove even? (1 2 3 4)) ;(1 3)
  • reduce Это тоже самое, что и foldLeft. Есть вариант, где первый элемент становится начальным значением или что-то другое: (reduce + [1 2 3]) ;6 (reduce cons '() [1 2 3]) ; (3 2 1)
  • reductions Это reduce, который сохраняет все промежуточные значения (reductions + [1 2 3]) ; (1 3 6)
  • Recursion
  • Simple recursion Просто можно вызвать функцию из тела: (defn sum [[head & tail]] (if (nil? head) 0 (+ head (sum tail)))
  • mutual recursion Иногда нужно, чтобы две функции умели друг друга вызывать, на помощь приходит declare для второй функции. (declare fun-2) (defn fun-1 [i] (if (< i 3) i (fun-2 (- i 1)))) (defn fun-2 [i] (if (< i 2) i (fun-1 (- i 2))))
  • tail recursion Но если мы вызовем (sum (range 10000)) то получим StackOverflowError...
  • tail recursion Правильно использовать recur: (defn sum ([[head & tail] acc] (if (nil? head) acc (recur tail (+ acc head)))) ([coll] (sum coll 0)))
  • loop/recur Альтернативой может быть loop/recur: (defn sum [coll] (loop [[head & tail] coll acc 0] (if (nil? head) acc (recur tail (+ acc head)))))
  • Destructuring
  • Ugly way for binding coll Удобно ли так писать? (let [a (nth coll 0) b (nth coll 1) c (nth coll 2)] (doSmth ...))
  • Destructuring! Что-то очень похожее на pattern matching: (let [[a b c] coll] (doSmth ...))
  • tail Мы уже видели пример ранее: (let [[a b & tail] coll] (doSmth ...))
  • maps destructuring Для maps это тоже возможно: (def my-map {"Clojure" :lang}) (let [{lang :lang} my-map] (doSmth ...))
  • Strings Destructuring доступен и для строк: (defn foo [[a & tail]] (= (clojure.string/upper-case a) (str a)))
  • :as keyword Иногда саму коллекцию тоже хочется иметь в арсенале, тогда на помощь приходит :as (defn [[head & tail :as coll]] (doSomth ...))
  • destructuring everywhere defn, fn и let - это там, где это доступно. Но из-за того, что многое приводится макросами к этим конструкциям, destructuring доступен очень много где.
  • Java compatibility
  • Calling methods Это крайне просто: (.methodName object) (.toLowerCase "AbC") ; "abc"
  • Methods with args (.methodName object args) (.equals "a" "b")
  • java.lang.Class Символ ссылающийся на имя класса также дает нам и сам класс (compare .class, classOf) (map #(.getName %) (.getMethods Boolean))
  • static things Static field: (Math/PI) Static method: (ClassName/methodName args) (Thread/activeCount) ; 1
  • Getting all together Наконец-то классический HelloWorld! (.println (System/out) "Hello, World!")
  • Operator . Есть и альтернатива всему этому… (.toUpperCase "abc") (. "abc" toUpperCase) (Thread/activeCount) (. Thread activeCount) (.equals "a" "b") (. "a" equals "b")
  • Operator .. Последовательные вызовы можно объединить: (. (. System out) println "Hello, World!") (.. System out (println "Hello, World!"))
  • operator new Есть старый добрый оператор: (new ClassName args) (new Object) (new String "text") (new java.util.HashMap)
  • dot syntax Но есть и альтернатива… (ClassName. args) (Object.) (String. "text") (java.util.HashMap.)
  • for example (let [h (java.util.HashMap.)] (.put h "a" "b") (.get h "a"))
  • import Есть и возможность импортировать имена: (import [java.util HashMap ArrayList]) (HashMap.) (ArrayList.)
  • operator doto Что делает этот оператор? (doto (HashMap.) (.put 1 "one") (.put 2 "two"))
  • instance? Аналог instanceof/isInstanceOf (instance? HashSet (HashSet.))
  • Java classes in Clojure
  • reify С помощью этого оператора можно создавать анонимные классы: (reify InterfaceName (methodName [this args] body))
  • reify А также для нескольких интерфейсов: (reify InterfaceName (methodName [this args] body) AnotherInterface (method-2 [this args] body-2))
  • Runnable (def runnable (reify Runnable (run [this] (println "text")))) (.start (Thread. runnable))
  • Array compatibility (make-array (Integer/TYPE) 4) (make-array String 3) (to-array [1 2 3]) ;untyped: java.lang.Object[] (into-array [1 2 3]) ;typed: java.lang.Long[]
  • Declaring class in Clojure
  • gen-class Класс объявить легко: (gen-class :name my.class.Name) (compile 'my.class) (my.className.)
  • :methods Объявить: :methods [[methodName [String] String]] И реализовать: (defn -methodName [arg] (str "x " arg))
  • :prefix Можно добавить префикс для понятности: :prefix prefix Тогда реализовать: (defn prefix-methodName [arg] (str "x " arg))
  • :init/:constructors Объявить имя для конструктора, и сигнатуры конструкторов: :init init :constructors [[String] [] [String String] [String]]
  • :state При создании класс можно записать некоторый state, который можно вернуть как [[super-call] state] из конструктора. Вызывать можно так: :state state (.state this)
  • :implements/:extends Указать супер класс: :extends ClassName Можно также объявить список интерфейсов: :implements [java.io.Serializable]
  • :gen-class Желательно все это использовать в макросе ns, где и указать все, что требуется: (ns my.namespace (:gen-class))
  • Polymorphism
  • Example (def circle {:type ::circle :r 10}) (def rect {:type ::rectangle :w 10 :h 20}) (defn area [{type :type :as shape}] (cond (= type ::circle) (* Math/PI (:r shape) (:r shape)) (= type ::rectangle) (* (:w shape) (:h shape))))
  • solution: multimethods ● Runtime polymorphism ● Позволяет dispatching по совершенно любому объекту
  • defmulti Объявление очень простое: (defmulti function-name "Doc string" dispatch-fn)
  • defmethod За объявлением следуют реализации: (defmethod fName1 match-1 [this] ...) (defmethod fName2 match-2 [this] ...)
  • our example (defmulti area :type) (defmethod area ::circle [circle] (* Math/PI (:r circle) (:r circle))) (defmethod area ::rectangle [rect] (* (:w rect) (:h rect)))