clojure #1
DESCRIPTION
Clojure первая лекция.TRANSCRIPT
![Page 1: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/1.jpg)
Clojure #1Introduction to clojure
![Page 2: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/2.jpg)
Why Clojure?
● For JVM with Java interoperability● Instant run/reloading● Functional● Lisp features (macros, expressive)● built-in STM● Dynamic● Good performance
![Page 3: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/3.jpg)
Important tools
● Leiningen - Clojure build tool● REPL, nREPL● Editors: IDEA (La Clojure, Cursive), Emacs,
Light Table
![Page 4: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/4.jpg)
Basics
![Page 5: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/5.jpg)
Functions
Очень простой синтаксис:
(fn []
4)
![Page 6: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/6.jpg)
Function call
Любая функция вызывается в prefix нотации:
(+ 1 1)
В данном случае + это функция, а единицы это аргументы.
![Page 7: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/7.jpg)
Expressions
В Clojure, как практически и в Scala все является выражением, то есть возвращает значение:
(if condition
then-branch
else-branch)
![Page 8: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/8.jpg)
Literals
● "a string" - String
● :key - Keyword
● 'symbol - Symbol
● \newline, \c - Character
● nil - No value
● true, false - Booleans
● Numbers like in Java
![Page 9: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/9.jpg)
Data literals
● [1 2 3] - Vector
● {:key value :key1 value1} - Map
● #{:key :key1} - Set
● #() - Анонимная функция (fn)
![Page 10: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/10.jpg)
Definitions
Определяет “переменные”:
(def x (+ 1 (- 3 1)))
![Page 11: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/11.jpg)
Named functions
(defn foo
"This is documentation"
[arguments]
body)
Параметры анонимных функций определяются также.
![Page 12: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/12.jpg)
Higher order functions
Можно в качестве параметров передавать и другие функции, например:
(map inc [1 2 3])
Или анонимный вариант:
(map (fn [i] (+ i 1)) [1 2 3])
![Page 13: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/13.jpg)
Scoped definitions (let)
Можно определить переменные, которые будут видны лишь внутри s-expr:
(def sum-result
(let [pi Math/PI]
(/ (* pi pi) 6)))
![Page 14: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/14.jpg)
Conrol structures
![Page 15: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/15.jpg)
Ранее уже видели, else branch должен обязательно присутствовать
if
![Page 16: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/16.jpg)
do
Если нужно выполнить несколько выражений прежде, чем что-то вернуть. Признак side effects:
(do
expr1
expr2
return-expression)
![Page 17: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/17.jpg)
when
Всегда возвращает nil. Комбинация if только с then branch и do.
(when (even? 2)
expr1
expr2)
![Page 18: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/18.jpg)
if-let
Комбинация let и if с проверкой на nil/false:
(def France {:capital "Paris"})
(if-let [capital (:capital France)]
(println "Capital is " capital)
(println "Capital is empty"))
![Page 19: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/19.jpg)
cond
Является альтернативой для else-if цепочек:
(defn foo [n]
(cond
(> n 0) "positive"
(< n 0) "negative"
:else "zero"))
![Page 20: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/20.jpg)
More Clojure basics
![Page 21: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/21.jpg)
Следующий код легко может быть переписан:
(fn [e] (fn1 (fn2 e)))
Короче будет так:
(comp fn1 fn2)
Function composition
![Page 22: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/22.jpg)
Function application
Есть более длинная форма для вызова функции, ее можно использовать, например, в макросах:
(apply + [1 2 3])
![Page 23: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/23.jpg)
Namespaces
Каждый символ определен в каком-то namespace, его можно задать с помощью вызова ns:
(ns mylib.core)
![Page 24: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/24.jpg)
:requre
С помощью этого ключа можно добавить в namespace элементы из других namespaces:
(ns foo
(:require
clojure.test
[clojure.string :as str]))
![Page 25: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/25.jpg)
: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
![Page 26: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/26.jpg)
:only
Для того, чтобы избежать подобных проблем, можно использовать ключ :only
(ns foo (:use
[clojure.string :only [join]]))
![Page 27: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/27.jpg)
:import
С помощью этого ключа можно добавлять классы из Java:
(ns some.foo.space
"This is namespace doc"
(:import (java.util Date
GregorianCalendar)))
![Page 28: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/28.jpg)
Clojure data structures
![Page 29: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/29.jpg)
Списки определяются так:
'(1 2 3)
'("Scala" "Kotlin" "Erlang"
"Clojure")
Lists
![Page 30: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/30.jpg)
Vectors
Вектора уже ранее определяли, это аналог массивов в Clojure.
![Page 31: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/31.jpg)
Sets
Множества задаются двумя способами
#{1 2 3}
(set [1 2 3])
![Page 32: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/32.jpg)
Maps
Также уже ранее обсуждали:
{:id 55
:name "Clojure"
:is-dynamic true}
![Page 33: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/33.jpg)
Immutability
Все структуры данных в Clojure неизменяемы.Чаще всего, вновь создаваемые, структуры данных используют предыдущие версии, но все равно это может быть медленно.
![Page 34: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/34.jpg)
Transient
Для performance critical single-threaded кусков кода, можно написать все быстрее:
(defn vrange [n]
(loop [i 0 v (transient [])]
(if (< i n)
(recur (inc i) (conj! v i))
(persistent! v))))
![Page 35: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/35.jpg)
Vectors and Maps basics
![Page 36: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/36.jpg)
Для векторов достает элемент по индексу.Для Maps достает элемент по ключу.
(get [1 2 3] 1) ;2
(get {:one 1} :one) ;1
get-in принимает вектор, и выполняет последовательно get(get-in [1 [1 2] 3] [1 1]) ;2
get
![Page 37: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/37.jpg)
assoc
Для векторов добавляет новый элемент по индексу (возвращает новый вектор).Для Maps добавляет новую пару key/value
(assoc [] 0 1) ;[1]
(assoc {} :key :value)
;{:key :value}
![Page 38: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/38.jpg)
dissoc
Удаляет элемент по ключу в Maps:
(dissoc {:key :value} :key) ; {}
![Page 39: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/39.jpg)
keys/vals
Для Maps мы можем вытащить keys и values:
(keys {:a :b :c :d}); (:a :c)
(vals {:a :b :c :d}); (:b :d)
![Page 40: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/40.jpg)
merge
Также можно объединять несколько Maps, перекрывая значения слева направо
(merge {:a :x :c :x}
{:a :b}
{:a :c :e :f})
; {:a :c :c :x :e :f}
![Page 41: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/41.jpg)
merge-with
Если мы хотим перекрывать справа налево, то это тоже возможно:
(merge-with (fn [a b] a)
{:a :x :c :x}
{:a :b}
{:a :c :e :f})
; {:a :x :c :x :e :f}
![Page 42: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/42.jpg)
All collections basics
![Page 43: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/43.jpg)
Образуется от слова construct, может добавлять первый элемент к спискам и векторам
(cons 1 [1 2]) ; [1 1 2]
(cons 1 '(1 2)) ; (1 1 2)
cons
![Page 44: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/44.jpg)
conj
Добавляет элемент туда, где это удобнее в плане реализации коллекции:
(conj [1 2] 1) ; [1 2 1]
(conj '(1 2) 1) ; (1 1 2)
![Page 45: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/45.jpg)
concat
Объединяет две последовательности в один список
(concat [1 2] '(3 4)) ; (1 2 3 4)
![Page 46: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/46.jpg)
disj
Удаляет элемент из множества (и только!)
(disj #{:a :b} :a) ; #{:b}
![Page 47: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/47.jpg)
seq
Превращает любую коллекцию (включая Java collections, arrays) в seq коллекцию.
Что важно, пустая коллекция превращается в nil.
![Page 48: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/48.jpg)
Advanced collection operations
![Page 49: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/49.jpg)
Разбивает коллекцию на части определенной длины. Можно указать шаг, тогда части смогут перекрываться:
(partition 2 [1 2 3 4 5])
; ((1 2) (3 4))
partition
![Page 50: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/50.jpg)
flatten
Собирает одну коллекцию из коллекции коллекций:
(flatten [\a [\b] [\c \d]])
; (\a \b \c \d)
![Page 51: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/51.jpg)
frequencies
Возвращает частоту, встречающихся элементов:
(frequencies [1 2 3 2 1 2])
; {1 2, 2 3, 3 1}
![Page 52: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/52.jpg)
every?
Проверяет, что все элементы удовлетворяют некоторому предикату:
(every? even? [2 4 6]) ; true
(every? even? [1 2 3]) ; false
![Page 53: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/53.jpg)
some
true если хотя бы один элемент удовлетворяет предикату:
(some even? [2 4 5]) ; true
(some even? [1 5 3]) ; nil
![Page 54: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/54.jpg)
for comprehensions
Создает ленивую коллекцию
(for [x (range 2)
y (range 2)] [x y])
; [0 0] [0 1] [1 0] [1 1]
(for [x (range 3)
:while (even? x)] x)
; [2]
![Page 55: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/55.jpg)
doseq
Поэтому для side effects нужно использовать doseq, в котором они предполагаются, и функция всегда возвращает nil.
![Page 56: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/56.jpg)
Functional collections
![Page 57: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/57.jpg)
Для преобразования всех элементов коллекции можно использовать обычную функцию map:(defn fun [i] (+ 1 i))
(map fun [1 2 3])
map
![Page 58: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/58.jpg)
mapcat
Аналогично flatMap в Scala, в Clojure есть mapcat.(defn fun[i] (repeat i i))
(mapcat fun [1 2 3])
Получится (1 2 2 3 3 3)
![Page 59: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/59.jpg)
filter and remove
Две по сути одинаковые функции, только отличаются условием предиката(filter even? (1 2 3 4))
;(2 4)
(remove even? (1 2 3 4))
;(1 3)
![Page 60: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/60.jpg)
reduce
Это тоже самое, что и foldLeft. Есть вариант, где первый элемент становится начальным значением или что-то другое:(reduce + [1 2 3]) ;6
(reduce cons '() [1 2 3])
; (3 2 1)
![Page 61: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/61.jpg)
reductions
Это reduce, который сохраняет все промежуточные значения(reductions + [1 2 3])
; (1 3 6)
![Page 62: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/62.jpg)
Recursion
![Page 63: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/63.jpg)
Просто можно вызвать функцию из тела:(defn sum
[[head & tail]]
(if (nil? head) 0
(+ head (sum tail)))
Simple recursion
![Page 64: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/64.jpg)
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))))
![Page 65: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/65.jpg)
Но если мы вызовем(sum (range 10000))
то получим StackOverflowError...
tail recursion
![Page 66: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/66.jpg)
tail recursion
Правильно использовать recur:(defn sum
([[head & tail] acc]
(if (nil? head) acc
(recur tail (+ acc head))))
([coll] (sum coll 0)))
![Page 67: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/67.jpg)
loop/recur
Альтернативой может быть loop/recur:(defn sum [coll]
(loop [[head & tail] coll
acc 0]
(if (nil? head) acc
(recur tail (+ acc head)))))
![Page 68: Clojure #1](https://reader033.vdocuments.net/reader033/viewer/2022052307/559b89951a28ab72158b467c/html5/thumbnails/68.jpg)
Homework1. Напишите функцию call-twice, которая берет на вход функцию и параметр, и вызывает эту функцию два раза (не composition).2. Напишите функцию, которая читает из файла (используйте slurp), затем выводит текст консоль, и возвращает этот текст.3. Напишите def cube-anonymous, в который присвоена функция, которая возводит число в куб.4. Напишите функцию, которая на вход принимает два seq, и возвращает объединенные развернутые последовательности (concat + reverse)5. Напишите функцию, которая возвращает, есть ли элемент в seq (contains? не подходит, так как проверяет наличие индекса)6. Напишите функцию, которая по двум последовательностям выводит все различных элементов пары в консоль (сравнение это = или not=)7. Напишите функцию, которая возвращает seq повторений элемента elem n раз.