clojure #1

68
Clojure #1 Introduction to clojure

Upload: alexander-podkhalyuzin

Post on 07-Jul-2015

254 views

Category:

Technology


2 download

DESCRIPTION

Clojure первая лекция.

TRANSCRIPT

Page 1: Clojure #1

Clojure #1Introduction to clojure

Page 2: Clojure #1

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

Important tools

● Leiningen - Clojure build tool● REPL, nREPL● Editors: IDEA (La Clojure, Cursive), Emacs,

Light Table

Page 4: Clojure #1

Basics

Page 5: Clojure #1

Functions

Очень простой синтаксис:

(fn []

4)

Page 6: Clojure #1

Function call

Любая функция вызывается в prefix нотации:

(+ 1 1)

В данном случае + это функция, а единицы это аргументы.

Page 7: Clojure #1

Expressions

В Clojure, как практически и в Scala все является выражением, то есть возвращает значение:

(if condition

then-branch

else-branch)

Page 8: Clojure #1

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

Data literals

● [1 2 3] - Vector

● {:key value :key1 value1} - Map

● #{:key :key1} - Set

● #() - Анонимная функция (fn)

Page 10: Clojure #1

Definitions

Определяет “переменные”:

(def x (+ 1 (- 3 1)))

Page 11: Clojure #1

Named functions

(defn foo

"This is documentation"

[arguments]

body)

Параметры анонимных функций определяются также.

Page 12: Clojure #1

Higher order functions

Можно в качестве параметров передавать и другие функции, например:

(map inc [1 2 3])

Или анонимный вариант:

(map (fn [i] (+ i 1)) [1 2 3])

Page 13: Clojure #1

Scoped definitions (let)

Можно определить переменные, которые будут видны лишь внутри s-expr:

(def sum-result

(let [pi Math/PI]

(/ (* pi pi) 6)))

Page 14: Clojure #1

Conrol structures

Page 15: Clojure #1

Ранее уже видели, else branch должен обязательно присутствовать

if

Page 16: Clojure #1

do

Если нужно выполнить несколько выражений прежде, чем что-то вернуть. Признак side effects:

(do

expr1

expr2

return-expression)

Page 17: Clojure #1

when

Всегда возвращает nil. Комбинация if только с then branch и do.

(when (even? 2)

expr1

expr2)

Page 18: Clojure #1

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

cond

Является альтернативой для else-if цепочек:

(defn foo [n]

(cond

(> n 0) "positive"

(< n 0) "negative"

:else "zero"))

Page 20: Clojure #1

More Clojure basics

Page 21: Clojure #1

Следующий код легко может быть переписан:

(fn [e] (fn1 (fn2 e)))

Короче будет так:

(comp fn1 fn2)

Function composition

Page 22: Clojure #1

Function application

Есть более длинная форма для вызова функции, ее можно использовать, например, в макросах:

(apply + [1 2 3])

Page 23: Clojure #1

Namespaces

Каждый символ определен в каком-то namespace, его можно задать с помощью вызова ns:

(ns mylib.core)

Page 24: Clojure #1

:requre

С помощью этого ключа можно добавить в namespace элементы из других namespaces:

(ns foo

(:require

clojure.test

[clojure.string :as str]))

Page 25: Clojure #1

: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

:only

Для того, чтобы избежать подобных проблем, можно использовать ключ :only

(ns foo (:use

[clojure.string :only [join]]))

Page 27: Clojure #1

:import

С помощью этого ключа можно добавлять классы из Java:

(ns some.foo.space

"This is namespace doc"

(:import (java.util Date

GregorianCalendar)))

Page 28: Clojure #1

Clojure data structures

Page 29: Clojure #1

Списки определяются так:

'(1 2 3)

'("Scala" "Kotlin" "Erlang"

"Clojure")

Lists

Page 30: Clojure #1

Vectors

Вектора уже ранее определяли, это аналог массивов в Clojure.

Page 31: Clojure #1

Sets

Множества задаются двумя способами

#{1 2 3}

(set [1 2 3])

Page 32: Clojure #1

Maps

Также уже ранее обсуждали:

{:id 55

:name "Clojure"

:is-dynamic true}

Page 33: Clojure #1

Immutability

Все структуры данных в Clojure неизменяемы.Чаще всего, вновь создаваемые, структуры данных используют предыдущие версии, но все равно это может быть медленно.

Page 34: Clojure #1

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

Vectors and Maps basics

Page 36: Clojure #1

Для векторов достает элемент по индексу.Для 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

assoc

Для векторов добавляет новый элемент по индексу (возвращает новый вектор).Для Maps добавляет новую пару key/value

(assoc [] 0 1) ;[1]

(assoc {} :key :value)

;{:key :value}

Page 38: Clojure #1

dissoc

Удаляет элемент по ключу в Maps:

(dissoc {:key :value} :key) ; {}

Page 39: Clojure #1

keys/vals

Для Maps мы можем вытащить keys и values:

(keys {:a :b :c :d}); (:a :c)

(vals {:a :b :c :d}); (:b :d)

Page 40: Clojure #1

merge

Также можно объединять несколько Maps, перекрывая значения слева направо

(merge {:a :x :c :x}

{:a :b}

{:a :c :e :f})

; {:a :c :c :x :e :f}

Page 41: Clojure #1

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

All collections basics

Page 43: Clojure #1

Образуется от слова construct, может добавлять первый элемент к спискам и векторам

(cons 1 [1 2]) ; [1 1 2]

(cons 1 '(1 2)) ; (1 1 2)

cons

Page 44: Clojure #1

conj

Добавляет элемент туда, где это удобнее в плане реализации коллекции:

(conj [1 2] 1) ; [1 2 1]

(conj '(1 2) 1) ; (1 1 2)

Page 45: Clojure #1

concat

Объединяет две последовательности в один список

(concat [1 2] '(3 4)) ; (1 2 3 4)

Page 46: Clojure #1

disj

Удаляет элемент из множества (и только!)

(disj #{:a :b} :a) ; #{:b}

Page 47: Clojure #1

seq

Превращает любую коллекцию (включая Java collections, arrays) в seq коллекцию.

Что важно, пустая коллекция превращается в nil.

Page 48: Clojure #1

Advanced collection operations

Page 49: Clojure #1

Разбивает коллекцию на части определенной длины. Можно указать шаг, тогда части смогут перекрываться:

(partition 2 [1 2 3 4 5])

; ((1 2) (3 4))

partition

Page 50: Clojure #1

flatten

Собирает одну коллекцию из коллекции коллекций:

(flatten [\a [\b] [\c \d]])

; (\a \b \c \d)

Page 51: Clojure #1

frequencies

Возвращает частоту, встречающихся элементов:

(frequencies [1 2 3 2 1 2])

; {1 2, 2 3, 3 1}

Page 52: Clojure #1

every?

Проверяет, что все элементы удовлетворяют некоторому предикату:

(every? even? [2 4 6]) ; true

(every? even? [1 2 3]) ; false

Page 53: Clojure #1

some

true если хотя бы один элемент удовлетворяет предикату:

(some even? [2 4 5]) ; true

(some even? [1 5 3]) ; nil

Page 54: Clojure #1

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

doseq

Поэтому для side effects нужно использовать doseq, в котором они предполагаются, и функция всегда возвращает nil.

Page 56: Clojure #1

Functional collections

Page 57: Clojure #1

Для преобразования всех элементов коллекции можно использовать обычную функцию map:(defn fun [i] (+ 1 i))

(map fun [1 2 3])

map

Page 58: Clojure #1

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

filter and remove

Две по сути одинаковые функции, только отличаются условием предиката(filter even? (1 2 3 4))

;(2 4)

(remove even? (1 2 3 4))

;(1 3)

Page 60: Clojure #1

reduce

Это тоже самое, что и foldLeft. Есть вариант, где первый элемент становится начальным значением или что-то другое:(reduce + [1 2 3]) ;6

(reduce cons '() [1 2 3])

; (3 2 1)

Page 61: Clojure #1

reductions

Это reduce, который сохраняет все промежуточные значения(reductions + [1 2 3])

; (1 3 6)

Page 62: Clojure #1

Recursion

Page 63: Clojure #1

Просто можно вызвать функцию из тела:(defn sum

[[head & tail]]

(if (nil? head) 0

(+ head (sum tail)))

Simple recursion

Page 64: Clojure #1

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

Но если мы вызовем(sum (range 10000))

то получим StackOverflowError...

tail recursion

Page 66: Clojure #1

tail recursion

Правильно использовать recur:(defn sum

([[head & tail] acc]

(if (nil? head) acc

(recur tail (+ acc head))))

([coll] (sum coll 0)))

Page 67: Clojure #1

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

Homework1. Напишите функцию call-twice, которая берет на вход функцию и параметр, и вызывает эту функцию два раза (не composition).2. Напишите функцию, которая читает из файла (используйте slurp), затем выводит текст консоль, и возвращает этот текст.3. Напишите def cube-anonymous, в который присвоена функция, которая возводит число в куб.4. Напишите функцию, которая на вход принимает два seq, и возвращает объединенные развернутые последовательности (concat + reverse)5. Напишите функцию, которая возвращает, есть ли элемент в seq (contains? не подходит, так как проверяет наличие индекса)6. Напишите функцию, которая по двум последовательностям выводит все различных элементов пары в консоль (сравнение это = или not=)7. Напишите функцию, которая возвращает seq повторений элемента elem n раз.