clojure #1

Post on 07-Jul-2015

239 Views

Category:

Technology

2 Downloads

Preview:

Click to see full reader

DESCRIPTION

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

TRANSCRIPT

Clojure #1Introduction to clojure

Why Clojure?

● For JVM with Java interoperability● Instant run/reloading● Functional● Lisp features (macros, expressive)● built-in STM● Dynamic● Good performance

Important tools

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

Light Table

Basics

Functions

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

(fn []

4)

Function call

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

(+ 1 1)

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

Expressions

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

(if condition

then-branch

else-branch)

Literals

● "a string" - String

● :key - Keyword

● 'symbol - Symbol

● \newline, \c - Character

● nil - No value

● true, false - Booleans

● Numbers like in Java

Data literals

● [1 2 3] - Vector

● {:key value :key1 value1} - Map

● #{:key :key1} - Set

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

Definitions

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

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

Named functions

(defn foo

"This is documentation"

[arguments]

body)

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

Higher order functions

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

(map inc [1 2 3])

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

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

Scoped definitions (let)

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

(def sum-result

(let [pi Math/PI]

(/ (* pi pi) 6)))

Conrol structures

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

if

do

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

(do

expr1

expr2

return-expression)

when

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

(when (even? 2)

expr1

expr2)

if-let

Комбинация let и if с проверкой на nil/false:

(def France {:capital "Paris"})

(if-let [capital (:capital France)]

(println "Capital is " capital)

(println "Capital is empty"))

cond

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

(defn foo [n]

(cond

(> n 0) "positive"

(< n 0) "negative"

:else "zero"))

More Clojure basics

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

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

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

(comp fn1 fn2)

Function composition

Function application

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

(apply + [1 2 3])

Namespaces

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

(ns mylib.core)

:requre

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

(ns foo

(:require

clojure.test

[clojure.string :as str]))

: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

:only

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

(ns foo (:use

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

:import

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

(ns some.foo.space

"This is namespace doc"

(:import (java.util Date

GregorianCalendar)))

Clojure data structures

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

'(1 2 3)

'("Scala" "Kotlin" "Erlang"

"Clojure")

Lists

Vectors

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

Sets

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

#{1 2 3}

(set [1 2 3])

Maps

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

{:id 55

:name "Clojure"

:is-dynamic true}

Immutability

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

Transient

Для performance critical single-threaded кусков кода, можно написать все быстрее:

(defn vrange [n]

(loop [i 0 v (transient [])]

(if (< i n)

(recur (inc i) (conj! v i))

(persistent! v))))

Vectors and Maps basics

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

assoc

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

(assoc [] 0 1) ;[1]

(assoc {} :key :value)

;{:key :value}

dissoc

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

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

keys/vals

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

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

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

merge

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

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

{:a :b}

{:a :c :e :f})

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

merge-with

Если мы хотим перекрывать справа налево, то это тоже возможно:

(merge-with (fn [a b] a)

{:a :x :c :x}

{:a :b}

{:a :c :e :f})

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

All collections basics

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

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

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

cons

conj

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

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

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

concat

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

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

disj

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

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

seq

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

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

Advanced collection operations

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

(partition 2 [1 2 3 4 5])

; ((1 2) (3 4))

partition

flatten

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

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

; (\a \b \c \d)

frequencies

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

(frequencies [1 2 3 2 1 2])

; {1 2, 2 3, 3 1}

every?

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

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

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

some

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

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

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

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]

doseq

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

Functional collections

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

(map fun [1 2 3])

map

mapcat

Аналогично flatMap в Scala, в Clojure есть mapcat.(defn fun[i] (repeat i i))

(mapcat fun [1 2 3])

Получится (1 2 2 3 3 3)

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

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

[[head & tail]]

(if (nil? head) 0

(+ head (sum tail)))

Simple recursion

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))))

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

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

tail recursion

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)))))

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

top related