# Чист Clojure: Изчерпателно Ръководство ## Съдържание 1. [Въведение в Clojure](#1-въведение-в-clojure) 2. [Първи стъпки](#2-първи-стъпки) 3. [Основен синтаксис и форми](#3-основен-синтаксис-и-форми) 4. [Структури от данни](#4-структури-от-данни) 5. [Функции](#5-функции) 6. [Управление на потока](#6-управление-на-потока) 7. [Серии и мързеливо оценяване](#7-серии-и-мързеливо-оценяване) 8. [Деструктуриране](#8-деструктуриране) 9. [Пространства от имена](#9-пространства-от-имена) 10. [Макроси](#10-макроси) 11. [Конкурентност](#11-конкурентност) 12. [Протоколи и записи](#12-протоколи-и-записи) 13. [Многометодни функции](#13-многометодни-функции) 14. [Тестване](#14-тестване) 15. [REPL](#15-repl) 16. [Core.async](#16-coreasync) 17. [Добри практики](#17-добри-практики) 18. [Индекс](#18-индекс) --- ## 1. Въведение в Clojure ### 1.1 Какво е Clojure? Clojure е съвременен, динамичен и функционален език за програмиране, който работи на Java Virtual Machine (JVM), .NET Common Language Runtime (CLR) и JavaScript двигатели чрез ClojureScript. Създаден от Rich Hickey през 2007 г., Clojure е диалект на Lisp, който набляга на неизменяемостта, функционалното програмиране и кратката изразителност. ### 1.2 Ключови характеристики на Clojure #### 1.2.1 Неизменяемост по подразбиране В Clojure всички структури от данни са неизменяеми по подразбиране. Когато "модифицирате" структура от данни, вие всъщност създавате нова версия с желаните промени, докато оригиналът остава непроменен. Този подход води до по-безопасни конкурентни програми и по-чист код. ```clojure (def original [1 2 3]) (def modified (conj original 4)) ;; original => [1 2 3] ;; modified => [1 2 3 4] ``` #### 1.2.2 Функционално програмиране Clojure насърчава чисти функции без странични ефекти. Функциите са обекти от първи клас, които могат да бъдат предавани като аргументи, връщани от други функции и композирани заедно. ```clojure (def double (partial * 2)) (def add-ten (partial + 10)) (def transform (comp double add-ten)) (transform 5) ;; => 30 ``` #### 1.2.3 Наследство от Lisp Като диалект на Lisp, Clojure наследява силата на макросите и еднаквото представяне на код като данни. Всичко е израз, който връща стойност, а синтаксисът е прост и последователен. #### 1.2.4 Полиморфизъм по време на изпълнение Clojure предоставя множество механизми за полиморфизъм: - **Протоколи**: Дефинират сигнатури на методи за типове данни - **Многометодни функции**: Диспечират въз основа на произволни критерии - **Записи**: Конкретни типове данни, които имплементират протоколи ### 1.3 Защо Чист Clojure? Въпреки че Clojure работи на JVM и има отлична Java интеграция, тази книга се фокусира върху **чист Clojure** — основните функции на езика, които не разчитат на Java интеграция. Този подход: - Учи фундаменталните концепции на Clojure - Прави кода преносим (включително ClojureScript) - Насърчава мисленето в парадигмата на Clojure - Избягва ненужно смесване на парадигми ### 1.4 Философията на Clojure Clojure се придържа към няколко принципа: 1. **Простота**: Трудните неща трябва да бъдат прости, простите неща трябва да бъдат тривиални 2. **Неизменяемост**: Предпочитайте неизменяеми структури от данни за безопасност и конкурентност 3. **Абстракция**: Изграждайте слоеве на абстракция за управление на сложността 4. **Ориентираност към изрази**: Всичко е израз, който връща стойност --- ## 2. Първи стъпки ### 2.1 Инсталация #### 2.1.1 Използване на CLI инструменти Препоръчителният начин за инсталиране на Clojure е чрез официалния CLI инструмент: **Linux/macOS:** ```bash curl -O https://download.clojure.org/install/linux-install-1.11.1.1202.sh chmod +x linux-install-1.11.1.1202.sh ./linux-install-1.11.1.1202.sh ``` **macOS (Homebrew):** ```bash brew install clojure ``` #### 2.1.2 Ръчна инсталация Изтеглете Clojure JAR файла и го използвайте директно: ```bash java -jar clojure-1.11.1.jar ``` ### 2.2 Вашият първи Clojure проект #### 2.2.1 Създаване на проект с deps.edn Създайте нова директория за вашия проект и добавете файл `deps.edn`: ```clojure {:deps {org.clojure/clojure {:mvn/version "1.11.1"}}} ``` Стартирайте Clojure: ```bash clj -M ``` #### 2.2.2 Основи на REPL REPL (Read-Eval-Print Loop) е вашата основна среда за разработка: ```clojure user=> (+ 1 2 3) 6 user=> (println "Hello, Clojure!") Hello, Clojure! nil user=> (def message "Hello, World!") #'user/message user=> message "Hello, World!" ``` ### 2.3 Настройка на редактора Популярни редактори за Clojure разработка: - **VS Code**: Разширение Calva - **Emacs**: Режим CIDER - **Vim/Neovim**: Добавка Conjure - **IntelliJ**: Добавка Cursive --- ## 3. Основен синтаксис и форми ### 3.1 S-Изрази Кодът на Clojure се пише като **s-изрази** (символни изрази), които са вложени списъци: ```clojure (оператор операнди...) ``` Първият елемент е операторът (функция, макрос или специална форма), а останалите са операнди. ```clojure (+ 1 2) ;; Събиране: 3 (* 2 3 4) ;; Умножение: 24 (< 1 2 3) ;; Сравнение: true (and true false) ;; Логическо И: false ``` ### 3.2 Данни като код В Lisp данните и кодът са едно и също нещо. Това означава, че можете да манипулирате програмите си като данни: ```clojure '(+ 1 2 3) ;; Цитиран списък: (+ 1 2 3) (list + 1 2) ;; Списък със + функцията и числа ``` ### 3.3 Специални форми Специалните форми са примитиви, които не могат да бъдат изразени като функции, защото имат специални правила за оценяване. #### 3.3.1 def Дефинира глобална променлива: ```clojure (def x 10) (def name "Clojure") (def items [1 2 3]) ``` #### 3.3.2 let Създава локални свързвания: ```clojure (let [x 10 y 20] (+ x y)) ;; => 30 ``` #### 3.3.3 if Условно изпълнение: ```clojure (if условие тогава-израз else-израз) ``` #### 3.3.4 quote Предотвратява оценяване: ```clojure (quote (+ 1 2)) ;; => (+ 1 2) '(+ 1 2) ;; Кратка форма: (+ 1 2) ``` ### 3.4 Правила за оценяване 1. Числа, низове, булеви стойности, nil и ключови думи **се оценяват като себе си** 2. Символи **се оценяват като стойността** на променливата, която именуват 3. Списъци **се оценяват като извиквания на функции** (ако първият елемент е извикаем) 4. Цитираните изрази **предотвратяват оценяване** ### 3.5 Коментари ```clojure ;; Едноредов коментар ;; Многоредов коментар ;; (няма специален многоредов синтаксис, ;; просто използвайте няколко едноредови коментара) (comment "Това е коментар блок, който няма да бъде оценен" (+ 1 2)) ``` ### 3.6 Интервали и форматиране - Clojure е безразличен към интервалите (с изключение в рамките на символи) - Стандартна конвенция: една интервал след отваряща скоба, преди затваряща - Подравнявайте аргументите вертикално за четливост: ```clojure (do-something arg1 arg2 arg3) ``` --- ## 4. Структури от данни Clojure предоставя богат набор от неизменяеми структури от данни. Разбирането им е фундаментално за писане на идиоматичен Clojure. ### 4.1 Числа #### 4.1.1 Целочислени типове ```clojure 42 ;; Десетично 017 ;; Осмично (15) 0x2A ;; Шестнадесетично (42) 2r101010 ;; Двоично (42) ``` #### 4.1.2 Числа с плаваща запетая ```clojure 3.14 6.022e23 ``` #### 4.1.3 Рационални числа Clojure запазва точността с рационални числа: ```clojure 1/3 ;; Тип рационално 22/7 ;; Приближение на pi (/ 1 3) ;; 1/3 ``` ### 4.2 Низове ```clojure "Hello, World!" "Multi-line string" ;; Конкатенация (str "Hello" " " "World") ;; => "Hello World" ;; Подниз (subs "Hello" 0 5) ;; => "Hello" ;; Функции за низове (count "Clojure") ;; => 7 (reverse "Clojure") ;; => "erujolC" ``` ### 4.3 Символи (Characters) ```clojure \a ;; Символ a \newline ;; Нов ред \space ;; Интервал ``` ### 4.4 Булеви стойности ```clojure true false nil ;; Представлява отсъствие на стойност ``` Правила за истинност: - Всичко с изключение на `false` и `nil` е истина - `and`, `or`, `if`, `when` използват това правило ### 4.5 Ключови думи (Keywords) Ключовите думи са интернирани низове, използвани като идентификатори, често за ключове в map-ове: ```clojure :foo :bar :user/name ;; Пространство от имена на ключова дума ::local-key ;; Автоматично с пространство от имена ``` Ключовите думи се оценяват като себе си и могат да се използват като функции за търсене на стойности в map-ове. ### 4.6 Символи (Symbols) Символите се оценяват като променливите, които именуват: ```clojure 'x ;; Символ x (цитиран) (def x 10) ;; Дефинира променлива x със стойност 10 x ;; Оценява се до 10 ``` ### 4.7 Списъци (Lists) Списъците са свързани списъци, ефективни за последователен достъп в началото: ```clojure '(1 2 3) ;; Цитирайте, за да предотвратите оценяване (list 1 2 3) ;; Създава списък '(+ 1 2) ;; Списък съдържащ символа + ;; Достъп (first '(1 2 3)) ;; => 1 (second '(1 2 3)) ;; => 2 (rest '(1 2 3)) ;; => (2 3) (nth '(1 2 3) 0) ;; => 1 ;; Модификация (връща нов списък) (cons 0 '(1 2 3)) ;; => (0 1 2 3) (concat '(1 2) '(3 4)) ;; => (1 2 3 4) ``` ### 4.8 Вектори (Vectors) Векторите са индексирани колекции, ефективни за случаен достъп: ```clojure [1 2 3 4 5] (vector 1 2 3) ;; => [1 2 3] ;; Достъп (get [10 20 30] 1) ;; => 20 ([10 20 30] 1) ;; => 20 (достъп като с ключова дума) (first [1 2 3]) ;; => 1 (second [1 2 3]) ;; => 2 (last [1 2 3]) ;; => 3 ;; Модификация (връща нов вектор) (conj [1 2] 3) ;; => [1 2 3] (pop [1 2 3]) ;; => [1 2] (assoc [1 2 3] 1 20) ;; => [1 20 3] (subvec [1 2 3 4 5] 1 3) ;; => [2 3] ``` ### 4.9 Map-ове (Maps) Map-овете са асоциативни структури ключ-стойност: ```clojure {:name "Alice" :age 30} (hash-map :a 1 :b 2 :c 3) (assoc {:a 1} :b 2) ;; => {:a 1 :b 2} (dissoc {:a 1 :b 2} :a) ;; => {:b 2} (get {:a 1} :a) ;; => 1 ({:a 1} :a) ;; => 1 (:a {:a 1}) ;; => 1 (ключовите думи са функции!) ;; Вложен достъп (get-in {:user {:address {:city "Sofia"}}} [:user :address :city]) ;; => "Sofia" ;; Сливане (merge {:a 1} {:b 2} {:c 3}) ;; => {:a 1 :b 2 :c 3} ``` ### 4.10 Множества (Sets) Множествата са колекции от уникални стойности: ```clojure #{1 2 3} (hash-set 1 2 3 2 1) ;; => #{1 2 3} (set [1 2 2 3 3 3]) ;; => #{1 2 3} ;; Операции (conj #{1 2} 3) ;; => #{1 2 3} (disj #{1 2 3} 2) ;; => #{1 3} (contains? #{1 2 3} 2) ;; => true (get #{1 2 3} 2) ;; => 2 (clojure.set/union #{1 2} #{2 3}) ;; => #{1 2 3} (clojure.set/intersection #{1 2 3} #{2 3 4}) ;; => #{2 3} (clojure.set/difference #{1 2 3} #{2 3}) ;; => #{1} ``` ### 4.11 Структуриране на данни ```clojure ;; Представяне на потребител с map (def user {:name "John" :email "john@example.com" :roles [:admin :user]}) ;; Вложени данни (def company {:name "TechCorp" :employees [{:name "Alice" :dept "Engineering"} {:name "Bob" :dept "Sales"}] :locations {:HQ "New York" :branch "Boston"}}) ``` ### 4.12 Библиотека за колекции Основни функции за колекции, които работят еднакво върху различни структури: ```clojure ;; Предикати (empty? []) ;; => true (empty? [1 2 3]) ;; => false (every? even? [2 4 6]) ;; => true (some odd? [2 4 5 6]) ;; => true (not-empty [1 2 3]) ;; => [1 2 3] (not-empty []) ;; => nil ;; Брой (count [1 2 3]) ;; => 3 (count {:a 1 :b 2}) ;; => 2 ;; Конверсия (vec '(1 2 3)) ;; => [1 2 3] (list [1 2 3]) ;; => (1 2 3) (set [1 2 2 3]) ;; => #{1 2 3} (mapv inc [1 2 3]) ;; => [2 3 4] ``` --- ## 5. Функции ### 5.1 Дефиниране на функции #### 5.1.1 Основен синтаксис ```clojure (defn greeting "Връща поздравително съобщение" [name] (str "Hello, " name "!")) (greeting "World") ;; => "Hello, World!" ``` #### 5.1.2 Множество арности Функциите могат да имат различен брой аргументи: ```clojure (defn add ([x] (add x 0)) ([x y] (+ x y)) ([x y z] (+ x y z))) (add 5) ;; => 5 (add 5 3) ;; => 8 (add 1 2 3) ;; => 6 ``` #### 5.1.3 Променлив брой аргументи Използвайте `&` за останали параметри: ```clojure (defn sum [& numbers] (reduce + numbers)) (sum 1 2 3 4 5) ;; => 15 ``` ### 5.2 Анонимни функции ```clojure (fn [x] (* x x)) #(* % %) ;; Имплицитен аргумент #(* %1 %2) ;; Множество аргументи #(reduce + %&) ;; Останали аргументи ``` ### 5.3 Функции от по-висок ред Функции, които приемат или връщат други функции: ```clojure (def double #( * % 2)) (def square #(* % %)) (map double [1 2 3 4]) ;; => (2 4 6 8) (map square [1 2 3 4]) ;; => (1 4 9 16) (filter even? [1 2 3 4 5 6]) ;; => (2 4 6) (reduce + [1 2 3 4 5]) ;; => 15 (reduce max [3 1 4 1 5]) ;; => 5 ;; Композиране на функции (def transform (comp square double)) (transform 3) ;; => 36 (3*2=6, 6*6=36) ``` ### 5.4 Затваряния (Closures) Функции, които улавят своята среда: ```clojure (defn make-adder [x] (fn [y] (+ x y))) (def add-5 (make-adder 5)) (add-5 10) ;; => 15 (add-5 3) ;; => 8 ;; Пример за брояч (defn make-counter [] (let [count (atom 0)] {:increment #(swap! count inc) :decrement #(swap! count dec) :value #(deref count)})) ``` ### 5.5 Пред- и пост-условия ```clojure (defn absolute-value [n] {:pre [(number? n)] :post [(>= % 0)]} (if (neg? n) (- n) n)) ``` ### 5.6 Многометодни функции чрез defn Въпреки че истинските многометодни функции използват `defmulti` и `defmethod`, обикновените функции могат да симулират dispatch по поведение: ```clojure (defn process [x] (cond (string? x) (clojure.string/upper-case x) (number? x) (inc x) :else "unknown")) ``` --- ## 6. Управление на потока ### 6.1 Разклоняване #### 6.1.1 if / if-not ```clojure (if условие тогава-израз else-израз) (if (pos? -5) "положително" "не е положително") ;; => "не е положително" ;; if-not е просто (if (not условие)...) (if-not (even? 4) "нечетно" "четно") ;; => "четно" ``` #### 6.1.2 when / when-not Единично разклонение без else: ```clojure (when (pos? 5) (println "Положително!") (inc 5)) ;; => 6 (when-not (neg? 3) "неотрицателно") ;; => "неотрицателно" ``` #### 6.1.3 cond Множество условия: ```clojure (defn classify [n] (cond (neg? n) "отрицателно" (zero? n) "нула" (even? n) "положително четно" :else "положително нечетно")) (classify -5) ;; => "отрицателно" (classify 0) ;; => "нула" (classify 4) ;; => "положително четно" (classify 7) ;; => "положително нечетно" ``` #### 6.1.4 condp Dispatch базиран на предикат: ```clojure (defn respond [msg] (condp = msg "hello" "Здравей!" "bye" "Довиждане!" "how are you?" "Добре!" "Неизвестно съобщение")) (respond "hello") ;; => "Здравей!" ``` #### 6.1.5 case Dispatch с константно време (използва хеш сравнение): ```clojure (defn http-status [code] (case code 200 "OK" 301 "Преместен постоянно" 404 "Не е намерен" 500 "Вътрешна грешка на сървъра" "Неизвестен")) (http-status 200) ;; => "OK" (http-status 999) ;; => "Неизвестен" ``` ### 6.2 Итерация #### 6.2.1 Рекурсия ```clojure (defn factorial [n] (if (<= n 1) 1 (* n (factorial (dec n))))) ;; Със recur (оптимизирано за опашка) (defn factorial [n] (letfn [(fac [n acc] (if (<= n 1) acc (recur (dec n) (* acc n))))] (fac n 1))) ``` #### 6.2.2 loop/recur Явна итерация с опашкова рекурсия: ```clojure (loop [i 0 result []] (if (= i 10) result (recur (inc i) (conj result i)))) ;; => [0 1 2 3 4 5 6 7 8 9] ``` #### 6.2.3 for (list comprehension) ```clojure (for [x (range 5) :let [y (* x x)] :when (even? y)] y) ;; => (0 4 16) (for [x [:a :b :c] y [1 2]] [x y]) ;; => ([a 1] [a 2] [b 1] [b 2] [c 1] [c 2]) ``` #### 6.2.4 doseq (странични ефекти) ```clojure (doseq [x (range 3) y (range 3)] (println [x y])) ;; Отпечатва: ;; [0 0] ;; [0 1] ;; [0 2] ;; ... ``` ### 6.3 Обработка на изключения ```clojure (try (/ 1 0) (catch ArithmeticException e (str "Грешка: " (.getMessage e))) (finally (println "Почистване"))) ;; С throw (try (throw (ex-info "Потребителска грешка" {:code 123})) (catch Exception e (ex-data e))) ;; => {:code 123} ``` ### 6.4 do Изпълнява множество изрази, връща последния: ```clojure (do (println "Страничен ефект") (println "Още един") (+ 1 2)) ;; => 3 ``` --- ## 7. Серии и мързеливо оценяване ### 7.1 Абстракцията Серия (Sequence) Clojure предоставя унифициран интерфейс за последователни колекции. Ключовите функции са: - `first` - Първи елемент - `rest` - Всички елементи след първия - `cons` - Добавя елемент в началото ```clojure ;; Работи върху списъци, вектори, низове, map-ове, множества и т.н. (first [1 2 3]) ;; => 1 (rest [1 2 3]) ;; => (2 3) (cons 0 [1 2 3]) ;; => (0 1 2 3) (first "hello") ;; => \h (rest "hello") ;; => (\e \l \l \o) (first {:a 1 :b 2}) ;; => [:a 1] ``` ### 7.2 Мързеливи серии Мързеливите серии се изчисляват при поискване, което позволява: -безкрайни серии - ефективност на паметта - оптимизация на производителността ```clojure ;; range произвежда безкрайна мързелива серия (take 10 (range)) ;; => (0 1 2 3 4 5 6 7 8 9) ;; Поредица на Фибоначи (def fibs (lazy-cat [0 1] (map + fibs (rest fibs)))) (take 10 fibs) ;; => (0 1 1 2 3 5 8 13 21 34) ;; iterate (take 5 (iterate inc 0)) ;; => (0 1 2 3 4) (take 5 (iterate #(* 2 %) 1)) ;; => (1 2 4 8 16) ``` ### 7.3 Функции за серии #### 7.3.1 map Трансформира всеки елемент: ```clojure (map inc [1 2 3]) ;; => (2 3 4) (map + [1 2 3] [4 5 6]) ;; => (5 7 9) (map str "abc") ;; => ("a" "b" "c") ``` #### 7.3.2 filter / remove Селектира/отхвърля елементи: ```clojure (filter even? (range 10)) ;; => (0 2 4 6 8) (remove even? (range 10)) ;; => (1 3 5 7 9) (filterv even? (range 10)) ;; => [0 2 4 6 8] (вектор) ``` #### 7.3.3 reduce Обработва елементи с натрупване: ```clojure (reduce + [1 2 3 4 5]) ;; => 15 (reduce + 10 [1 2 3]) ;; => 16 (с начална стойност) (reduce (fn [[sum cnt] x] [(+ sum x) (inc cnt)]) [0 0] [1 2 3 4 5]) ;; => [15 5] ``` #### 7.3.4 fold Паралелно намаляване (използва reducers): ```clojure (require '[clojure.core.reducers :as r]) (r/fold + (range 1000)) ``` #### 7.3.5 mapcat Map-ва и след това изравнява: ```clojure (mapcat reverse [[1 2] [3 4] [5 6]]) ;; => (2 1 4 3 6 5) ``` #### 7.3.6 take / drop ```clojure (take 3 (range 10)) ;; => (0 1 2) (drop 3 (range 10)) ;; => (3 4 5 6 7 8 9) (take-while pos? [3 2 1 0 -1]) ;; => (3 2 1) (drop-while pos? [3 2 1 0 -1]) ;; => (0 -1) (split-at 3 (range 5)) ;; => [(0 1 2) (3 4)] ``` #### 7.3.7 flatten / partition ```clojure (flatten [[1 2] [3 [4 5]]]) ;; => (1 2 3 4 5) (partition 2 (range 8)) ;; => ((0 1) (2 3) (4 5) (6 7)) (partition-all 3 (range 10)) ;; => ((0 1 2) (3 4 5) (6 7 8) (9)) (partition-by even? [1 2 3 4 5 6]) ;; => ((1) (2 3 4) (5 6)) ``` #### 7.3.8 Interpose / Interleave ```clojure (interpose "," ["a" "b" "c"]) ;; => ("a" "," "b" "," "c") (apply str (interpose "," ["a" "b" "c"])) ;; => "a,b,c" (interleave [1 2 3] [:a :b :c]) ;; => (1 :a 2 :b 3 :c) ``` #### 7.3.9 distinct / sort / shuffle ```clojure (distinct [1 2 2 3 3 3 4]) ;; => (1 2 3 4) (sort [3 1 4 1 5 9 2]) ;; => (1 1 2 3 4 5 9) (sort-by :age [{:age 30} {:age 20} {:age 40}]) ;; => ({:age 20} {:age 30} {:age 40}) (shuffle (range 5)) ;; => случайна наредба ``` ### 7.4 Създаване на серии ```clojure (range) ;; Безкрайни 0, 1, 2, ... (range 5) ;; (0 1 2 3 4) (range 1 10 2) ;; (1 3 5 7 9) start, end, step (repeat 5 :x) ;; (:x :x :x :x :x) (repeatedly 5 #(rand-int 100)) ;; Случайни стойности (cycle [:a :b]) ;; Безкрайни (:a :b :a :b ...) ``` ### 7.5 Обхождане на колекции ```clojure ;; tree-seq: обхожда вложена структура (tree-seq sequential? seq [1 [2 [3 4]] 5]) ;; => ([1 [2 [3 4]] 5] 1 [2 [3 4]] 2 [3 4] 3 4 5) ;; flatten работи с tree-seq (flatten [1 [2 [3 4]] 5]) ;; => (1 2 3 4 5) ;; Postwalk и prewalk (require '[clojure.walk :as walk]) (walk/postwalk #(if (number? %) (* 2 %) %) [1 [2 3] 4]) ;; => [2 [4 6] 8] ``` --- ## 8. Деструктуриране Деструктурирането ви позволява да свързвате локални променливи към части от колекции. ### 8.1 Деструктуриране на вектори ```clojure (let [[a b c] [1 2 3]] (+ a b c)) ;; => 6 ;; Пропускане на елементи (let [[a _ c] [1 2 3]] c) ;; => 3 ;; Останал pattern (let [[a & rest] [1 2 3 4]] rest) ;; => (2 3 4) ;; Със стойности по подразбиране (let [[a b c d] [1 2]] [a b c d]) ;; => [1 2 nil nil] ;; Използване на :or за подразбиране (let [[a b :or {b 10}] [1]] b) ;; => 10 ``` ### 8.2 Деструктуриране на map-ове ```clojure (let [{a :a b :b} {:a 1 :b 2}] (+ a b)) ;; => 3 ;; Преименуване на ключове (let [{x :a y :b :as original} {:a 1 :b 2}] [x y original]) ;; => [1 2 {:a 1 :b 2}] ;; Със стойности по подразбиране (let [{name :name :or {name "Анонимен"}} {}] name) ;; => "Анонимен" ;; Използване на :keys за автоматично именуване (let [{:keys [name age city]} {:name "John" :age 30 :city "Boston"}] [name age city]) ;; => ["John" 30 "Boston"] ;; Използване на :strs за ключове низове (let [{:strs [name age]} {"name" "John" "age" 30}] name) ;; => "John" ;; Използване на :syms за ключове символи (let [{:syms [x y]} {'x 1 'y 2}] x) ;; => 1 ``` ### 8.3 Вложено деструктуриране ```clojure (let [[[x y] [a b]] [[1 2] [3 4]]] (+ x y a b)) ;; => 10 (let [{name :user {:keys [city state]} :address} {:user "John" :address {:city "Boston" :state "MA"}}] city) ;; => "Boston" ``` ### 8.4 Деструктуриране в параметри на функции ```clojure (defn process [[first second & rest]] {:first first :second second :rest rest}) (process [1 2 3 4 5]) ;; => {:first 1 :second 2 :rest (3 4 5)} (defn greet [{:keys [name age]}] (str "Здравей, " name "! На " age " години си.")) (greet {:name "Alice" :age 25}) ;; => "Здравей, Alice! На 25 години си." ``` ### 8.5 Деструктуриране с :as ```clojure (defn total [{:keys [a b c] :as numbers}] (+ a b c)) (total {:a 1 :b 2 :c 3 :d 4}) ;; => 6, numbers все още има :d ``` --- ## 9. Пространства от имена ### 9.1 Създаване и превключване на пространства от имена ```clojure (ns myapp.core) (ns myapp.utils (:require [clojure.string :as str])) ;; В REPL (in-ns 'myapp.core) ``` ### 9.2 Отнасяне и импортиране ```clojure (ns myapp.core (:require [clojure.string :as str] [clojure.set :as set] [clojure.walk :as walk]) (:import [java.util Date UUID])) ;; Java interop, показано за пълнота ``` ### 9.3 Често използвани директиви за namespace ```clojure (:require [module :as alias]) (:require [module :refer [fn1 fn2]]) (:require [module :refer :all]) ;; Избягвайте в production (:use [module]) ;; Остаряло, предпочитайте :require с :refer (:import [java.util Date]) ;; Java interop ``` ### 9.4 Опции на ns макроса | Опция | Предназначение | |--------|---------| | `:require` | Зарежда модули с незадължителен alias | | `:use` | Зарежда и отнася символи | | `:import` | Импортира Java класове | | `:refer-clojure` | Контролира referrals към core | | `:load` | Зарежда произволен код | | `:gen-class` | Генерира Java клас | ### 9.5 Работа с пространства от имена ```clojure ;; Създаване на var (def x 10) ;; Вземане на текущото namespace *ns* ;; => #namespace[user] ;; Resolve на символ (resolve 'x) ;; => #'user/x ;; Създаване на namespace (create-ns 'myapp.data) ;; Intern на var (intern 'myapp.data (symbol "y") 20) ;; Вземане на всички vars в namespace (ns-publics 'myapp.core) ``` ### 9.6 Добри практики за namespace 1. Едно namespace на файл 2. Използвайте смислени имена (напр. `myapp.http.client`) 3. Използвайте последователно aliasing 4. Минимизирайте `:use`, предпочитайте `:require` с `:refer` 5. Събирайте свързани кодове заедно --- ## 10. Макроси ### 10.1 Какво са макросите? Макросите са код, който трансформира код преди оценяване. Те получават неоценен код и връщат нов код за оценяване. ```clojure ;; Прост макрос (defmacro unless [condition & body] `(if (not ~condition) (do ~@body))) ;; Употреба (unless (= 1 2) (println "Математиката работи!") (+ 1 2)) ``` ### 10.2 Синтаксис цитат (Syntax Quote) Обратната кавичка (`) предотвратява оценяване и позволява темплейти: ```clojure (defmacro debug [expr] `(let [result ~expr] (println "Debug:" '~expr "=" result) result)) ``` ### 10.3 Unquoting - `~` (unquote) - Оценява и вмъква - `~@` (unquote-splicing) - Оценява и разгъва последователност ```clojure (defmacro with-logging [expr] `(do (println "Изпълнява:" '~expr) (let [result ~expr] (println "Резултат:" result) result))) ;; Splicing пример (defmacro chain [& forms] `(do ~@forms)) (chain (println "Първо") (println "Второ")) ``` ### 10.4 Кога да използваме макроси **Използвайте макроси когато:** - Трябва да контролирате оценяването (като `if`, `when`, `unless`) - Трябва да свързвате символи, не стойности (като `let`, `doseq`) - Трябва да правите изчисления по време на компилация **Използвайте функции когато:** - Логиката може да се изрази като трансформация на данни - Върнатата стойност е данни, не код ### 10.5 Разгъване на макроси ```clojure ;; Вижте какво произвежда макрос без да го изпълнявате (macroexpand '(when (> x 10) (println "Голям") (inc x))) ;; macroexpand-1 за една стъпка ``` ### 10.6 Често срещани модели за макроси #### 10.6.1 Анафорични макроси (имплицитно свързване) ```clojure (defmacro with-local-vars [& body] `(let [] ~@(map (fn [form] `(quote ~(transform form))) body))) ;; По-прост: threading macros (->> x (filter even?) (map inc) (take 5)) ``` #### 10.6.2 Условна компилация ```clojure (defmacro when-bind [[sym test] & body] `(let [~sym ~test] (when ~sym ~@body))) (when-bind [x (find-value data)] (process x)) ``` ### 10.7 Хигиена По подразбиране Clojure макросите са **хигиенични** - не изпускат нежелани свързвания. Въпреки това можете да създавате gensyms за ясен контрол: ```clojure (defmacro my-macro [] (let [temp# (gensym "temp")] `(let [~temp# 10] ~temp#))) ;; temp# auto-gensyms за всяка употреба ``` --- ## 11. Конкурентност Clojure предоставя множество безопасни модели за конкурентност. Всички структури от данни в Clojure са неизменяеми, което елиминира цели класове от грешки свързани с конкурентността. ### 11.1 Атоми (Atoms) Атомите предоставят синхронна, независима работа със състояние: ```clojure (def counter (atom 0)) ;; Четете стойността (deref counter) ;; => 0 @counter ;; => 0 ;; Обновявате с функция (swap! counter inc) ;; => 1 (swap! counter + 5) ;; => 6 ;; Нулирате към стойност (reset! counter 0) ;; => 0 ;; Обновяване с множество аргументи (swap! counter + 1 2 3) ;; => 6 ``` ### 11.2 Референции (Refs) Референциите предоставят синхронизирано, координирано състояние чрез Software Transactional Memory (STM): ```clojure (def account1 (ref 100)) (def account2 (ref 200)) ;; dosync създава транзакция (dosync (alter account1 - 50) (alter account2 + 50)) ;; Refs могат да бъдат модифицирани само в рамките на dosync ``` ### 11.3 Агенти (Agents) Агентите предоставят асинхронни, независими обновления на състояние: ```clojure (def logger (agent [])) ;; Изпратете обновление (асинхронно) (send logger conj "event-1") ;; Изчакайте завършване (await logger) ;; Send-off за блокиращи операции (send-off logger #(Thread/sleep 1000)) ``` ### 11.4 Променливи (Vars) Vars предоставят thread-local и namespace-scoped състояние: ```clojure (def ^:dynamic *max-connections* 100) ;; Динамично свързване (binding [*max-connections* 50] (*max-connections*)) ;; => 50 ;; Thread-local (def ^:dynamic *thread-id* nil) (defn get-thread-id [] (binding [*thread-id* (java.lang.Thread/currentThread)] *thread-id*)) ``` ### 11.5 Futures Futures изпълняват код конкурентно: ```clojure (def my-future (future (+ 1 2 3))) ;; Dereference за да получите резултата @my-future ;; => 6 ;; Проверете дали е завършило (future-done? my-future) ;; => true ;; Отказ (ако е възможно) ;; (future-cancel my-future) ``` ### 11.6 Promises и Delivered Promises са placeholders за единична стойност: ```clojure (def p (promise)) ;; Доставяте стойност (deliver p 42) ;; Блокирате докато бъде доставено @promise ;; => 42 ;; Timeout (deref p 1000 :timeout) ;; Връща :timeout след 1000ms ``` ### 11.7 Threads ```clojure ;; Стартирате thread (.start (Thread. #(println "Работи в thread"))) ;; С повече контрол (let [t (Thread. ^Runnable (fn [] (println "Thread тяло")))] (.start t)) ``` ### 11.8 Насоки за STM 1. Поддържайте транзакциите кратки 2. Избягвайте странични ефекти в транзакции 3. Използвайте commute за комутативни операции 4. Използвайте ref-set за прости присвоявания 5. Retry се случва автоматично при конфликт ```clojure ;; commute за комутативни операции (редът няма значение) (dosync (commence total count operation)) ``` --- ## 12. Протоколи и записи ### 12.1 Протоколи Протоколите дефинират сигнатури на методи, които типовете могат да имплементират: ```clojure (defprotocol Shape (area [this]) (perimeter [this])) (defprotocol Movable (move [this dx dy])) ``` ### 12.2 Записи Записите са конкретни типове данни, които могат да имплементират протоколи: ```clojure (defrecord Point [x y] Shape (area [this] 0) (perimeter [this] 0) Movable (move [this dx dy] (->Point (+ x dx) (+ y dy)))) ;; Създаване на инстанция (->Point 3 4) ;; => #user.Point{:x 3 :y 4} (Point. 3 4) ;; Java-style конструктор ;; Factory функция (автогенерирана) (map->Point {:x 10 :y 20}) ``` ### 12.3 Разширяване на съществуващи типове Разширете типове да имплементират протоколи: ```clojure (extend-protocol Shape java.awt.geom.Area (area [this] (.getBounds this)) nil (area [this] 0)) ;; extend за единични инстанции (defmethod area :default [this] (when (sequential? this) (count this))) ``` ### 12.4 Reify Създавате анонимни инстанции: ```clojure (def circle (reify Shape (area [this] (* Math/PI (.radius this) (.radius this))) (perimeter [this] (* 2 Math/PI (.radius this))) :radius 5)) ;; Не може лесно да улови външно състояние - използвайте records за това ``` --- ## 13. Многометодни функции Многометодните функции предоставят полиморфизъм чрез произволен dispatch: ### 13.1 Дефиниране на многометодни функции ```clojure (defmulti process type) (defmethod process :default [x] (str "Неизвестно: " x)) (defmethod process Number [x] (inc x)) (defmethod process String [x] (clojure.string/upper-case x)) ``` ### 13.2 Функции за dispatch ```clojure ;; Dispatch по стойност (defmulti kind identity) ;; Dispatch по множество стойности (defmulti describe (fn [x y] [(type x) (type y)])) ;; Dispatch по property (defrecord User [role]) (defmethod describe [:user :admin] [_] "Администратор") (defmethod describe [:user :guest] [_] "Гост") ``` ### 13.3 Йерархии ```clojure ;; Derive създава наследяване за dispatch (derive ::rect ::shape) (derive ::circle ::shape) (derive ::square ::rect) ;; Dispatch работи с йерархията (defmulti area :type) (defmethod area ::rect [r] (* (:width r) (:height r))) (defmethod area ::circle [c] (* Math/PI (:radius c) (:radius c))) ``` ### 13.4 remove-method ```clojure (remove-method process String) ``` --- ## 14. Тестване ### 14.1 Clojure.test ```clojure (ns myapp.core-test (:require [clojure.test :as t] [myapp.core :as core])) (t/deftest addition-test (t/testing "основно събиране" (t/is (= 4 (+ 2 2))) (t/is (= 5 (+ 2 2))) ;; Неуспех (t/are [x y] (= x y) 2 (+ 1 1) 4 (+ 2 2)))) (t/deftest collection-test (t/is (vector? [])) (t/is (empty? [])) (t/is (= 3 (count [1 2 3])))) ``` ### 14.2 Fixtures ```clojure (defn setup [f] (направете нещо преди) (f) (направете нещо след)) (t/use-fixtures :each setup) ;; Изпълнява за всеки тест (t/use-fixtures :once setup) ;; Изпълнява веднъж за всички тестове ``` ### 14.3 Пускане на тестове ```bash clojure -M:test lein test ``` ### 14.4 Генеративно тестване (test.check) ```clojure (require '[clojure.test.check :as tc] '[clojure.test.check.generators :as gen] '[clojure.test.check.properties :as prop]) (def sort-idempotent (prop/for-all [v (gen/vector gen/int)] (= (sort v) (sort (sort v))))) (tc/quick-check 100 sort-idempotent) ``` --- ## 15. REPL ### 15.1 Команди на REPL | Команда | Описание | |---------|---------| | `doc` | Преглед на документация | | `find-doc` | Търсене в документите | | `source` | Преглед на source код | | `pst` | Отпечатване на stack trace | | `apropos` | Търсене на символи | | `dir` | Списък на vars в namespace | ### 15.2 REPL работен процес ```clojure ;; Зареждане на код (require '[myapp.core :as core] :reload) ;; Изчистване на REPL състояние (remove-all-methods multimethod :default) ;; Хващане на изключения CompilerException ... ;; Pretty print (require '[clojure.pprint :as pp]) (pp/pprint data) ``` ### 15.3 Интеграция с редактор - **VS Code + Calva**: `:jack-in` за стартиране на REPL - **Emacs + CIDER**: `cider-jack-in` - **Vim + Conjure**: Свързва се автоматично --- ## 16. Core.async Core.async предоставя асинхронно програмиране с канали. ### 16.1 Канали ```clojure (require '[clojure.core.async :as async]) (def ch (async/chan)) ;; Поставяне на стойност (блокира ако буферът е пълен) (async/>!! ch "hello") ;; Вземане на стойност (блокира ако е празен) (async/ "hello" ;; Затваряне на канал (async/close! ch) ``` ### 16.2 Threaded Channels ```clojure ;; >!! и ! и ! out-ch "result")) ``` ### 16.4 Buffers ```clojure (async/chan 10) ;; Фиксиран буфер (async/chan (async/sliding-buffer 100)) ;; Пуска стари (async/chan (async/dropping-buffer 100)) ;; Пуска нови ``` ### 16.5 Pipeline ```clojure (async/pipeline-async 4 out-ch (fn [input ch] (async/go (async/>! ch (process input)))) in-ch) ``` --- ## 17. Добри практики ### 17.1 Организация на кода ```clojure ;; Типична структура на namespace (ns myapp.core (:require [myapp.util :as util] [myapp.spec :as spec] [clojure.string :as str]) (:import [java.util Date])) ;; Показано само за пълнота ``` ### 17.2 Неизменяеми данни Предпочитайте неизменяеми структури от данни. Когато мутация е нужна: - Използвайте atoms за независимо състояние - Използвайте refs със STM за координирано състояние - Избягвайте странични ефекти в чисти функции ### 17.3 Конвенции за именуване | Тип | Конвенция | Пример | |------|------------|--------| | Vars | kebab-case | `defn calculate-total` | | Класове/Записи | PascalCase | `defrecord UserProfile` | | Константи | UPPER-SNAKE | `def MAX-RETRY` | | Private vars | trailing underscore | `defn- internal-func` | | Dynamic vars | *заобиколени* | `def *max-connections*` | ### 17.4 Обработка на грешки ```clojure (defn safe-parse [s] (try (Long/parseLong s) (catch NumberFormatException _ nil))) ;; С ex-info за структурирани грешки (defn validate [x] (when (neg? x) (throw (ex-info "Трябва да е положително" {:value x})))) ``` ### 17.5 Съвети за производителност 1. Използвайте `transduce` вместо `into` + трансформация 2. Използвайте `mapv` когато се нуждаете от векторен резултат 3. Използвайте `filterv` за филтрирани вектори 4. Използвайте `reduce-kv` за итерация върху map 5. Помислете за `transducers` за ефективни трансформации ### 17.6 Threading Macros Направете кода по-четим: ```clojure ;; Thread-first (->) (-> user (assoc :last-login (java.time Instant/now)) (update :login-count inc) :last-login) ;; Thread-last (->>) (->> users (map :name) (filter #(.startsWith % "A")) (sort) (take 10)) ;; Thread-as (some->, some->>) (some-> {:user {:profile {:avatar "url"}}} :user :profile :avatar clojure.string/upper-case) ``` --- ## 18. Индекс ### A - `atom` - [11.1](#11-атоми-atoms) - `agent` - [11.3](#113-агенти-agents) - `and` - [3.5](#35-специални-форми) - `are` - [14.1](#141-clojuretest) - `apply` - [7.3.3](#733-reduce) - `as->` - [17.6](#176-threading-macros) - `assert` - [5.5](#55-пред--и-пост-условия) - `assoc` - [4.9](#49-map-ове-maps) - `async/chan` - [16.1](#161-канали) ### B - `binding` - [11.4](#114-променливи-vars) - `butlast` - [7.3.7](#737-interpose--interleave) ### C - `case` - [6.1.5](#615-case) - `comment` - [3.5](#35-коментари) - `comp` - [5.3](#53-функции-от-по-висок-ред) - `concat` - [4.7](#47-списъци-lists) - `cond` - [6.1.3](#613-cond) - `condp` - [6.1.4](#614-condp) - `conj` - [4.8](#48-вектори-vectors) - `cons` - [4.7](#47-списъци-lists) - `def` - [3.3.1](#331-def) - `defmacro` - [10.1](#101-какво-са-макросите) - `defmethod` - [13.1](#131-дефиниране-на-многометодни-функции) - `defmulti` - [13.1](#131-дефиниране-на-многометодни-функции) - `defn` - [5.1.1](#511-основен-синтаксис) - `defprotocol` - [12.1](#121-протоколи) - `defrecord` - [12.2](#122-записи) - `defref` - [11.2](#112-референции-refs) - `delay` - [11.6](#116-promises-и-delivered) - `destructure` - [8](#8-деструктуриране) - `disj` - [4.10](#410-множества-sets) - `dissoc` - [4.9](#49-map-ове-maps) - `doseq` - [6.2.4](#624-doseq-странични-ефекти) - `dosync` - [11.2](#112-референции-refs) - `dotimes` - [6.2.3](#623-for-list-comprehension) - `drop` - [7.3.6](#736-take--drop) - `drop-while` - [7.3.6](#736-take--drop) ### E - `empty?` - [4.12](#412-библиотека-за-колекции) - `extend-protocol` - [12.3](#123-разширяване-на-съществуващи-типове) - `extend-type` - [12.3](#123-разширяване-на-съществуващи-типове) ### F - `fdef` - [5.5](#55-пред--и-пост-условия) - `filter` - [7.3.2](#732-filter--remove) - `filterv` - [7.3.2](#732-filter--remove) - `find-doc` - [15.1](#151-команди-на-repl) - `first` - [4.7](#47-списъци-lists) - `flatten` - [7.3.7](#737-interpose--interleave) - `flip` - [11.2](#112-референции-refs) - `fn` - [5.2](#52-анонимни-функции) - `for` - [6.2.3](#623-for-list-comprehension) - `force` - [11.6](#116-promises-и-delivered) - `format` - [2.3](#23-настройка-на-редактора) - `future` - [11.5](#115-futures) ### G - `gen-class` - [9.4](#94-опции-на-ns-макроса) - `get` - [4.8](#48-вектори-vectors) - `get-in` - [4.9](#49-map-ове-maps) - `group-by` - [7.3.7](#737-interpose--interleave) ### H - `hash-map` - [4.9](#49-map-ове-maps) - `hash-set` - [4.10](#410-множества-sets) ### I - `if` - [3.3.3](#333-if) - `if-let` - [6.1.2](#612-when--when-not) - `if-not` - [6.1.1](#611-if--if-not) - `import` - [9.3](#93-отнасяне-и-импортиране) - `inc` - [4.2](#42-низове) - `indexed` - [7.3.7](#737-interpose--interleave) - `into` - [7.3.7](#737-interpose--interleave) - `interleave` - [7.3.8](#738-interpose--interleave) - `interpose` - [7.3.8](#738-interpose--interleave) - `iterate` - [7.2](#72-мързеливи-серии) ### J - `juxt` - [5.3](#53-функции-от-по-висок-ред) ### K - `keys` - [8.2](#82-деструктуриране-на-map-ове) ### L - `let` - [3.3.2](#332-let) - `letfn` - [5.1.3](#513-променлив-брой-аргументи) - `list` - [4.7](#47-списъци-lists) - `list*` - [4.7](#47-списъци-lists) - `load-file` - [15.2](#152-repl-работен-процес) - `loop` - [6.2.2](#622-looprecur) ### M - `macroexpand` - [10.5](#105-разгъване-на-макроси) - `macroexpand-1` - [10.5](#105-разгъване-на-макроси) - `map` - [7.3.1](#731-map) - `map-indexed` - [7.3.1](#731-map) - `mapcat` - [7.3.5](#735-mapcat) - `mapv` - [7.3.1](#731-map) - `max-key` - [5.3](#53-функции-от-по-висок-ред) - `merge` - [4.9](#49-map-ове-maps) - `merge-with` - [4.9](#49-map-ове-maps) - `meta` - [3.3.1](#331-def) - `min-key` - [5.3](#53-функции-от-по-висок-ред) - `mod` - [4.1.1](#411-целочислени-типове) ### N - `namespace` - [9.5](#95-работа-с-пространства-от-имена) - `neg?` - [4.2](#42-низове) - `nil?` - [4.4](#44-булеви-стойности) - `not` - [4.4](#44-булеви-стойности) - `not-empty` - [4.12](#412-библиотека-за-колекции) - `ns` - [9.1](#91-създаване-и-превключване-на-пространства-от-имена) - `ns-publics` - [9.5](#95-работа-с-пространства-от-имена) - `ns-resolve` - [9.5](#95-работа-с-пространства-от-имена) ### O - `or` - [3.5](#35-специални-форми) ### P - `parallelize` - [11.7](#117-насоки-за-stm) - `partition` - [7.3.7](#737-interpose--interleave) - `partition-all` - [7.3.7](#737-interpose--interleave) - `partition-by` - [7.3.7](#737-interpose--interleave) - `partial` - [5.3](#53-функции-от-по-висок-ред) - `peek` - [4.7](#47-списъци-lists) - `persist` - [7.2](#72-мързеливи-серии) - `pmap` - [7.3.1](#731-map) - `pop` - [4.8](#48-вектори-vectors) - `pos?` - [4.2](#42-низове) - `promise` - [11.6](#116-promises-и-delivered) ### Q - `quote` - [3.3.4](#334-quote) ### R - `rand` - [7.4](#74-създаване-на-серии) - `rand-int` - [7.4](#74-създаване-на-серии) - `range` - [7.4](#74-създаване-на-серии) - `recur` - [6.2.1](#621-рекурсия) - `reduce` - [7.3.3](#733-reduce) - `reduce-kv` - [7.3.3](#733-reduce) - `reductions` - [7.3.3](#733-reduce) - `ref` - [11.2](#112-референции-refs) - `ref-set` - [11.2](#112-референции-refs) - `release-pending-sends` - [11.3](#113-агенти-agents) - `remove` - [7.3.2](#732-filter--remove) - `repeat` - [7.4](#74-създаване-на-серии) - `repeatedly` - [7.4](#74-създаване-на-серии) - `replicate` - [7.4](#74-създаване-на-серии) - `require` - [9.3](#93-отнасяне-и-импортиране) - `reset!` - [11.1](#11-атоми-atoms) - `rest` - [4.7](#47-списъци-lists) - `reverse` - [7.3.9](#739-distinct--sort--shuffle) ### S - `select-keys` - [4.9](#49-map-ове-maps) - `send` - [11.3](#113-агенти-agents) - `send-off` - [11.3](#113-агенти-agents) - `seq` - [7.1](#71-абстракцията-серия-sequence) - `set` - [4.10](#410-множества-sets) - `set!` - [11.4](#114-променливи-vars) - `short-circuit` - [3.5](#35-специални-форми) - `shuffle` - [7.3.9](#739-distinct--sort--shuffle) - `shutdown-agents` - [11.3](#113-агенти-agents) - `some` - [7.3.2](#732-filter--remove) - `some->` - [17.6](#176-threading-macros) - `some-fn` - [5.3](#53-функции-от-по-висок-ред) - `sort` - [7.3.9](#739-distinct--sort--shuffle) - `sort-by` - [7.3.9](#739-distinct--sort--shuffle) - `split-at` - [7.3.6](#736-take--drop) - `split-with` - [7.3.6](#736-take--drop) - `str` - [4.2](#42-низове) - `subs` - [4.2](#42-низове) - `superiors` - [13.3](#133-йерархии) - `swap!` - [11.1](#11-атоми-atoms) ### T - `take` - [7.3.6](#736-take--drop) - `take-nth` - [7.3.6](#736-take--drop) - `take-while` - [7.3.6](#736-take--drop) - `test` - [14.1](#141-clojuretest) - `thread-bound?` - [11.4](#114-променливи-vars) - `throw` - [6.3](#63-обработка-на-изключения) - `tree-seq` - [7.5](#75-обхождане-на-колекции) - `try` - [6.3](#63-обработка-на-изключения) - `type` - [12.2](#122-записи) ### U - `update` - [4.9](#49-map-ове-maps) - `update-in` - [4.9](#49-map-ове-maps) - `use` - [9.3](#93-отнасяне-и-импортиране) ### V - `val` - [7.1](#71-абстракцията-серия-sequence) - `vals` - [4.9](#49-map-ове-maps) - `var` - [3.3.1](#331-def) - `var-get` - [11.4](#114-променливи-vars) - `var-set` - [11.4](#114-променливи-vars) - `vec` - [4.8](#48-вектори-vectors) - `vector` - [4.8](#48-вектори-vectors) - `vector-of` - [4.8](#48-вектори-vectors) - `volatile!` - [11.1](#11-атоми-atoms) ### W - `when` - [6.1.2](#612-when--when-not) - `when-bind` - [10.6.2](#1062-условна-компилация) - `when-first` - [6.1.2](#612-when--when-not) - `when-let` - [6.1.2](#612-when--when-not) - `when-not` - [6.1.2](#612-when--when-not) - `while` - [6.2](#62-итерация) ### Z - `zero?` - [4.2](#42-низове) - `zipmap` - [4.9](#49-map-ове-maps) --- ## Приложение А: Бърза справка ### Основни функции | Функция | Описание | |----------|-------------| | `inc` / `dec` | Инкремент / декремент | | `+` / `-` / `*` / `/` | Аритметика | | `=` / `==` / `not=` | Равенство | | `<` / `>` / `<=` / `>=` | Сравнение | | `and` / `or` / `not` | Логически | | `first` / `rest` / `next` | Операции със серии | | `cons` / `conj` / `concat` | Изграждане на колекции | | `map` / `filter` / `reduce` | Трансдюсъри | | `get` / `assoc` / `dissoc` | Операции с map | | `get-in` / `assoc-in` | Вложени операции | | `apply` / `partial` | Приложение на функция | | `comp` / `juxt` / `memoize` | Комбинатори на функции | ### Обобщение на структурите от данни | Тип | Литерал | Достъп | Неизменяем? | |------|---------|--------|------------| | Списък | `'(1 2 3)` | `first`, `nth` | Да | | Вектор | `[1 2 3]` | `get`, `nth` | Да | | Map | `{:a 1}` | `get`, `keys` | Да | | Множество | `#{1 2 3}` | `get`, `contains?` | Да | --- ## Приложение Б: Речник **Атом (Atom)** - Мутабилен контейнер, който осигурява синхронни, независими обновления. **Затваряне (Closure)** - Функция, която улавя и запазва достъп до променливи от обграждащия си обхват. **Деструктуриране (Destructuring)** - Свързване на локални променливи към части от колекция или map. **Хигиеничен макрос (Hygienic Macro)** - Макрос, който не изпуска нежелани свързвания. **Мързелива серия (Lazy Sequence)** - Серия, чиито елементи се изчисляват при поискване. **Протокол (Protocol)** - Именовано множество от сигнатури на методи, които типовете могат да имплементират. **Референция (Ref)** - Мутабилен контейнер, управляван от STM за координирани обновления. **S-Израз (S-Expression)** - Списък с кръгли скоби в Lisp синтаксиса. **STM** - Software Transactional Memory, модел за конкурентност използващ транзакции. **Var** - Мутабилен контейнер, който осигурява thread-local и namespace-scoped състояние. --- *Чист Clojure: Изчерпателно Ръководство* *Версия 1.0*