6. Function call
Любая функция вызывается в prefix
нотации:
(+ 1 1)
В данном случае + это функция, а единицы
это аргументы.
7. Expressions
В Clojure, как практически и в Scala все
является выражением, то есть возвращает
значение:
(if condition
then-branch
else-branch)
8. Literals
● "a string" - String
● :key - Keyword
● 'symbol - Symbol
● newline, c - Character
● nil - No value
● true, false - Booleans
● Numbers like in Java
9. Data literals
● [1 2 3] - Vector
● {:key value :key1 value1} - Map
● #{:key :key1} - Set
● #() - Анонимная функция (fn)
11. Named functions
(defn foo
"This is documentation"
[arguments]
body)
Параметры анонимных функций
определяются также.
12. Higher order functions
Можно в качестве параметров передавать и
другие функции, например:
(map inc [1 2 3])
Или анонимный вариант:
(map (fn [i] (+ i 1)) [1 2 3])
13. Scoped definitions (let)
Можно определить переменные, которые
будут видны лишь внутри s-expr:
(def sum-result
(let [pi Math/PI]
(/ (* pi pi) 6)))
15. if
Ранее уже видели, else branch должен
обязательно присутствовать
16. do
Если нужно выполнить несколько
выражений прежде, чем что-то вернуть.
Признак side effects:
(do
expr1
expr2
return-expression)
17. when
Всегда возвращает nil. Комбинация if только
с then branch и do.
(when (even? 2)
expr1
expr2)
18. if-let
Комбинация let и if с проверкой на nil/false:
(def France {:capital "Paris"})
(if-let [capital (:capital France)]
(println "Capital is " capital)
(println "Capital is empty"))
19. cond
Является альтернативой для else-if цепочек:
(defn foo [n]
(cond
(> n 0) "positive"
(< n 0) "negative"
:else "zero"))
22. Function application
Есть более длинная форма для вызова
функции, ее можно использовать, например,
в макросах:
(apply + [1 2 3])
23. Namespaces
Каждый символ определен в каком-то
namespace, его можно задать с помощью
вызова ns:
(ns mylib.core)
24. :requre
С помощью этого ключа можно добавить в
namespace элементы из других namespaces:
(ns foo
(:require
clojure.test
[clojure.string :as str]))
25. :use
Это сочетание :require и :refer. Использовать
следует с осторожностью, как пример:
(ns foo
(:use clojure.string))
WARNING: replace already refers to: #'clojure.core/replace in namespace: foo, being replaced by: #'clojure.string/replace
WARNING: reverse already refers to: #'clojure.core/reverse in namespace: foo, being replaced by: #'clojure.string/reverse
26. :only
Для того, чтобы избежать подобных
проблем, можно использовать ключ :only
(ns foo (:use
[clojure.string :only [join]]))
27. :import
С помощью этого ключа можно добавлять
классы из Java:
(ns some.foo.space
"This is namespace doc"
(:import (java.util Date
GregorianCalendar)))
32. Maps
Также уже ранее обсуждали:
{:id 55
:name "Clojure"
:is-dynamic true}
33. Immutability
Все структуры данных в Clojure
неизменяемы.
Чаще всего, вновь создаваемые, структуры
данных используют предыдущие версии, но
все равно это может быть медленно.
34. Transient
Для performance critical single-threaded
кусков кода, можно написать все быстрее:
(defn vrange [n]
(loop [i 0 v (transient [])]
(if (< i n)
(recur (inc i) (conj! v i))
(persistent! v))))
36. get
Для векторов достает элемент по индексу.
Для Maps достает элемент по ключу.
(get [1 2 3] 1) ;2
(get {:one 1} :one) ;1
get-in принимает вектор, и выполняет
последовательно get
(get-in [1 [1 2] 3] [1 1]) ;2
37. assoc
Для векторов добавляет новый элемент по
индексу (возвращает новый вектор).
Для Maps добавляет новую пару key/value
(assoc [] 0 1) ;[1]
(assoc {} :key :value)
;{:key :value}
39. keys/vals
Для Maps мы можем вытащить keys и values:
(keys {:a :b :c :d}); (:a :c)
(vals {:a :b :c :d}); (:b :d)
40. merge
Также можно объединять несколько Maps,
перекрывая значения слева направо
(merge {:a :x :c :x}
{:a :b}
{:a :c :e :f})
; {:a :c :c :x :e :f}
41. merge-with
Если мы хотим перекрывать справа налево,
то это тоже возможно:
(merge-with (fn [a b] a)
{:a :x :c :x}
{:a :b}
{:a :c :e :f})
; {:a :x :c :x :e :f}
57. map
Для преобразования всех элементов
коллекции можно использовать обычную
функцию map:
(defn fun [i] (+ 1 i))
(map fun [1 2 3])
58. mapcat
Аналогично flatMap в Scala, в Clojure есть
mapcat.
(defn fun[i] (repeat i i))
(mapcat fun [1 2 3])
Получится (1 2 2 3 3 3)
59. filter and remove
Две по сути одинаковые функции, только
отличаются условием предиката
(filter even? (1 2 3 4))
;(2 4)
(remove even? (1 2 3 4))
;(1 3)
60. reduce
Это тоже самое, что и foldLeft. Есть вариант,
где первый элемент становится начальным
значением или что-то другое:
(reduce + [1 2 3]) ;6
(reduce cons '() [1 2 3])
; (3 2 1)
61. reductions
Это reduce, который сохраняет все
промежуточные значения
(reductions + [1 2 3])
; (1 3 6)
63. Simple recursion
Просто можно вызвать функцию из тела:
(defn sum
[[head & tail]]
(if (nil? head) 0
(+ head (sum tail)))
64. 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))))
65. tail recursion
Но если мы вызовем
(sum (range 10000))
то получим StackOverflowError...
67. loop/recur
Альтернативой может быть loop/recur:
(defn sum [coll]
(loop [[head & tail] coll
acc 0]
(if (nil? head) acc
(recur tail (+ acc head)))))
68. Homework
1. Напишите функцию call-twice, которая берет на вход функцию и
параметр, и вызывает эту функцию два раза (не composition).
2. Напишите функцию, которая читает из файла (используйте slurp), затем
выводит текст консоль, и возвращает этот текст.
3. Напишите def cube-anonymous, в который присвоена функция, которая
возводит число в куб.
4. Напишите функцию, которая на вход принимает два seq, и возвращает
объединенные развернутые последовательности (concat + reverse)
5. Напишите функцию, которая возвращает, есть ли элемент в seq
(contains? не подходит, так как проверяет наличие индекса)
6. Напишите функцию, которая по двум последовательностям выводит все
различных элементов пары в консоль (сравнение это = или not=)
7. Напишите функцию, которая возвращает seq повторений элемента elem
n раз.