Files
bara-lang/books/clojure-book/bg/02-advanced.md
T
dimgigov 23252826a0 feat: add Clojure/Nim chapter to the book + restructure for GitLab
- New chapter 05-clojure-nim.md (EN + BG) covering:
  - Native compilation pipeline (Clojure → Nim → C → binary)
  - AI-powered development (error explanation, code generation)
  - JSON REPL for AI agents
  - loop/recur with real TCO
  - Cross-compilation: JS, shared libs, WASM
  - Persistent data structures (HAMT)
  - Concurrency: atoms, agents, channels
- Updated book README.md with Clojure/Nim focus
- Added Clojure/Nim terms to subject indices (EN + BG)
- Removed books/ from .gitignore so it can be pushed to GitLab
2026-05-08 23:32:11 +03:00

711 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Чист Clojure: Разширени теми
## Съдържание
1. [Разширени функции](#1-разширени-функции)
2. [Мързеливи серии - задълбочено](#2-мързеливи-серии---задълбочено)
3. [Трансдюсъри](#3-трансдюсъри)
4. [Specs и валидация](#4-specs-и-валидация)
5. [Протоколът Collection](#5-протоколът-collection)
6. [Reducibles](#6-reducibles)
7. [Паралелизъм](#7-паралелизъм)
8. [Оптимизация на производителността](#8-оптимизация-на-производителността)
9. [Индекс](#9-индекс)
---
## 1. Разширени функции
### 1.1 Вариадични функции
Функциите могат да приемат променлив брой аргументи:
```clojure
(defn print-all [& args]
(doseq [arg args]
(println arg)))
(print-all "a" "b" "c")
;; С задължителни аргументи
(defn greet [name & greeting-parts]
(str (clojure.string/join " " greeting-parts) ", " name "!"))
(greet "World" "Hello" "Good morning") ;; => "Hello Good morning, World!"
```
### 1.2 Rest параметри в детайли
Символът `&` улавя останалите аргументи като серия:
```clojure
(defn my-apply [f & args]
(apply f args))
;; Използване с деструктуриране
(defn first-two [[a b & rest]]
{:first a :second b :rest rest})
(first-two [1 2 3 4 5])
;; => {:first 1 :second 2 :rest (3 4 5)}
```
### 1.3 Аргументи от тип ключова дума
Clojure поддържа аргументи от тип ключова дума чрез деструктуриране:
```clojure
(defn configure [name & {:keys [debug verbose output]
:or {debug false verbose false output "stdout"}}]
{:name name :debug debug :verbose verbose :output output})
(configure "test" :debug true :verbose true :output "file.txt")
;; => {:name "test" :debug true :verbose true :output "file.txt"}
```
### 1.4 Взаимна рекурсия
Функциите могат да се извикват една друга:
```clojure
(defn even? [n]
(if (zero? n)
true
(odd? (dec n))))
(defn odd? [n]
(if (zero? n)
false
(even? (dec n))))
(even? 4) ;; => true
(odd? 3) ;; => true
```
### 1.5 Мемоизация
Кеширане на резултати от функции:
```clojure
(defn slow-fib [n]
(if (<= n 1)
n
(+ (slow-fib (- n 1))
(slow-fib (- n 2)))))
(def memo-fib (memoize slow-fib))
;; Разликата във времето е драматична за по-големи n
(time (memo-fib 35)) ;; Много по-бързо
```
### 1.6 Пред- и пост-условия
Валидиране на входове и изходи:
```clojure
(defn absolute-value [n]
{:pre [(number? n)]
:post [(number? %)
(>= % 0)]}
(if (neg? n)
(- n)
n))
(defn divide [a b]
{:pre [(not (zero? b)) "Делителят не може да е нула"]}
(/ a b))
```
### 1.7 Метаданни на функции
Функциите могат да имат метаданни:
```clojure
(defn ^:private internal-helper [x]
x)
(defn ^:deprecated old-function [x]
x)
;; Проверете метаданните
(meta #'internal-helper)
;; => {:private true, ...}
```
### 1.8 Арности и претоварване
```clojure
(defn arity-error []
(throw (ex-info "Невалидна арност" {})))
(defn complete
([x] (complete x 1))
([x y] (+ x y))
([x y z] (+ x y z)))
```
---
## 2. Мързеливи серии - задълбочено
### 2.1 Реализиране на серии
Мързеливите серии се реализират (оценяват) при необходимост:
```clojure
(def lazy-nats (range)) ;; Безкрайни
(take 10 lazy-nats) ;; Реализира първите 10
;; Принудете пълна реализация
(doall lazy-nats) ;; Опасно: безкрайна!
(doall (take 1000 lazy-nats))
```
### 2.2 Chunked серии
Мързеливите серии на Clojure са chunked (типично 32 елемента):
```clojure
;; Range създава chunked серии
(class (range 100)) ;; => clojure.lang.LongRange
;; Всеки chunk се реализира наведнъж
```
### 2.3 Lazy Cons и реализация
```clojure
;; cons създава мързелива серия
(def custom-seq (cons 1 (lazy-seq (cons 2 ()))))
;; lazy-seq отлага изчисленията
(defn fibs []
(cons 0
(cons 1
(map + (fibs) (rest (fibs))))))
```
### 2.4 Seqable обекти
Всеки обект може да бъде направен последователен чрез имплементиране на `seq`:
```clojure
(extend-type String
clojure.core.protocols/Coll
(coll [s] (seq s)))
;; Сега низовете работят със серийни функции
(map clojure.string/upper-case "hello")
;; => (\H \E \L \L \O)
```
### 2.5 Безкрайни серии
```clojure
;; Повтарящ се цикъл
(def repeating (cycle [:a :b :c]))
;; Повтаряне завинаги
(def ones (repeatedly 1))
(def randoms (repeatedly #(rand-int 100)))
;; Iterate - прилага функция към предишния резултат
(def powers-of-two (iterate #(* 2 %) 1))
(def collatz (iterate #(if (even? %) (/ % 2) (inc (* 3 %))) 1))
```
### 2.6 Производителност на сериите
```clojure
;; Не дръжте head на мързелива серия
(defn bad-sum []
(let [large-seq (range 10000000)]
(reduce + (take 10 large-seq)))) ;; Държи референция към цялата серия
(defn good-sum []
(reduce + (take 10 (range 10000000)))) ;; Head може да бъде GC'd
```
### 2.7 Eager vs Lazy
```clojure
;; mapcat може да бъде eager
(mapcat reverse [[1 2] [3 4]]) ;; => (2 1 4 3)
;; into принуждава реализация
(into [] (map inc (range 1000)))
;; into е ефективен - не създава междинни колекции
```
---
## 3. Трансдюсъри
Трансдюсърите са съставни, мързеливи трансформации, независими от входния контекст.
### 3.1 Създаване на трансдюсъри
```clojure
;; Без контекст
(def increment (map inc))
(def only-evens (filter even?))
;; Съставяне на трансдюсъри
(def transform (comp
(filter even?)
(map inc)
(take 10)))
```
### 3.2 Използване на трансдюсъри
```clojure
;; С всякаква последователна колекция
(transduce transform + (range 100))
;; => Сума на първите 10 четни числа + 1
(into [] transform (range 100))
;; => [3 5 7 9 11 13 15 17 19 21]
(sequence transform (range 100))
;; => Връща мързелива серия
```
### 3.3 Завършващи редукции
Някои трансдюсъри трябва да направят нещо в края:
```clojure
(def taking-transform
(fn [rf]
(let [n (volatile! 5)]
(fn
([] (rf))
([result] (rf result))
([result input]
(if (pos? @n)
(do (vswap! n dec)
(rf result input))
(reduced result)))))))
(transduce taking-transform + (range 100)) ;; => 10
```
### 3.4 Ранно прекратяване
```clojure
;; reduced увива стойност за спиране рано
(transduce (filter odd?) + (range 10))
;; => 25 (1+3+5+7+9)
;; Използвайте reduced? за проверка
(reduced? (reduced 5)) ;; => true
```
### 3.5 Cat и завършване
```clojure
(require '[clojure.core.protocols :as p])
;; Завършващата арност на редуциращата функция
(transduce
(map inc)
(fn
([result] result) ;; завършваща арност
([result input] (rf result input)))
[]
(range 5))
```
---
## 4. Specs и валидация
### 4.1 Въведение в Spec
Spec предоставя валидация по време на изпълнение и генеративно тестване (чрез `clojure.spec.gen`).
### 4.2 Дефиниране на Specs
```clojure
(require '[clojure.spec.alpha :as s])
(s/def ::name string?)
(s/def ::age (s/and int? #(>= % 0)))
(s/def ::person (s/keys :req [::name ::age]))
```
### 4.3 Конформиране
```clojure
(s/conform ::age 25) ;; => 25
(s/conform ::age -5) ;; => :clojure.spec.alpha/invalid
(s/conform ::person {::name "John" ::age 30})
;; => {::name "John" ::age 30}
```
### 4.4 Валидация с `valid?`
```clojure
(s/valid? ::age 25) ;; => true
(s/valid? ::age -5) ;; => false
(s/valid? ::person {::name "John" ::age 30}) ;; => true
```
### 4.5 Генеративно тестване
```clojure
(require '[clojure.spec.gen.alpha :as gen])
;; Генериране на стойности
(gen/generate (s/gen ::age))
(gen/sample (s/gen ::age))
;; Тестване със spec
(s/def ::email (s/and string?
#(re-find #"@" %)))
(s/fdef greet
:args (s/cat :name ::name)
:ret string?)
;; Пускане на генеративни тестове
(stest/instrument `greet)
```
### 4.6 Multi-spec
```clojure
(s/def ::shape (s/multi-spec :type keyword?))
(defmethod shape-spec :circle [_]
(s/keys :req [:radius]))
(defmethod shape-spec :rect [_]
(s/keys :req [:width :height]))
```
---
## 5. Протоколът Collection
### 5.1 Йерархия на колекциите
```
IPersistentCollection
IPersistentList
IPersistentVector
IPersistentMap
IPersistentSet
```
### 5.2 Ключови протоколи
```clojure
;; Sequential
(first coll)
(rest coll)
(next coll)
(cons item coll)
;; Counted
(count coll)
;; Indexed (Vectors)
(nth coll index)
(get coll index)
;; Associative (Maps)
(assoc coll key val)
(dissoc coll key)
(find coll key)
(keys coll)
(vals coll)
```
### 5.3 Разширяване на колекции
```clojure
;; Използване на reify
(def my-collection
(reify
clojure.core.protocols/Coll
(coll [this] this)
clojure.core.protocols/Indexed
(nth [this i] (get [10 20 30] i))))
(nth my-collection 1) ;; => 20
```
### 5.4 Персонализирани Reducibles
```clojure
(defrecord Range [start end]
clojure.core.protocols/Coll
(coll [this] (seq (range start end)))
(reduce + (Range. 1 10)) ;; => 45
```
---
## 6. Reducibles
Reduciers предоставят начин за извършване на паралелни редукции без мързеливи серии.
### 6.1 Използване на Reducers
```clojure
(require '[clojure.core.reducers :as r])
;; Паралелна map (автоматично паралелизира в fold)
(r/map inc (range 1000))
;; fold използва паралелна редукция
(r/fold + (r/map inc (range 1000000)))
```
### 6.2 Персонализирани Reducers
```clojure
;; fold изисква foldable колекция и комбинираща функция
(r/fold
(fn ([] 0) ([x y] (+ x y)))
(fn ([x] x) ([x y] (+ x y)))
(range 1000))
```
---
## 7. Паралелизъм
### 7.1 pmap
Паралелна map (мързелива):
```clojure
;; Като map, но се изпълнява паралелно
(time
(doall (pmap #(do (Thread/sleep 100) %) (range 10))))
;; Много по-бързо от обикновена map със задръстващи операции
```
### 7.2 Reducers за паралелизъм
```clojure
;; Сгъване с множество ядра
(r/fold 100 + (range 10000000))
;; Персонализирана комбинираща функция
(r/fold
100
(fn ([] 0) ([a b] (+ a b)))
(fn ([] 0) ([a b] (+ a b)))
(range 10000000))
```
### 7.3 Futures
```clojure
;; Независими паралелни задачи
(let [a (future (compute-a))
b (future (compute-b))]
[@a @b]) ;; Изчаква и двете
```
### 7.4 CompletableFuture (само бележка)
Забележка: Java's `CompletableFuture` изисква Java interop. Чисти алтернативи на Clojure включват:
- Core.async канали
- Manifold библиотека
- Promises с futures
---
## 8. Оптимизация на производителността
### 8.1 Persistent структури от данни
Persistent структурите от данни на Clojure споделят структура:
```clojure
;; Добавяне към вектор споделя повечето структура
(def v1 [1 2 3 4 5])
(def v2 (conj v1 6))
;; v1 и v2 споделят [1 2 3 4 5]
;; Само нови възли се създават за пътя към новия елемент
```
### 8.2 Transient структури от данни
За локални, временни мутации:
```clojure
(defn slow-accumulation []
(loop [coll []
i 0]
(if (= i 100000)
coll
(recur (conj coll i) (inc i)))))
(defn fast-accumulation []
(persistent!
(loop [coll (transient [])
i 0]
(if (= i 100000)
coll
(recur (conj! coll i) (inc i))))))
(time (count (slow-accumulation))) ;; По-бавно
(time (count (fast-accumulation))) ;; По-бързо
```
### 8.3 Chunked операции
```clojure
;; Предпочитайте chunked операции
(into [] (map inc (range 1000))) ;; Създава една междинна серия
(into [] (mapcat list (range 100))) ;; Изравнява мързеливо
```
### 8.4 Поддържане на аргументи eager
```clojure
;; Лошо: дръж head на серията
(def bad-result (map f large-collection))
;; Добро: обработвайте незабавно
(into [] (map f large-collection))
```
### 8.5 Batch обработка
```clojure
;; Вместо много малки операции
(doseq [x items]
(update-db x))
;; Помислете за batch-ване
(batch-update items)
```
### 8.6 Предварително зареждане и кеширане
```clojure
;; Мемоизация за скъпи изчисления
(def cached-expensive-lookup
(memoize (fn [k]
(compute-expensively k))))
;; Предварително зареждане при стартиране
(def initialized-data
(delay (load-and-process-data)))
```
### 8.7 Бенчмаркинг
```clojure
(require '[criterium.core :as c])
(c/quick-bench (reduce + (range 10000)))
;; Докладва mean, std deviation и т.н.
```
---
## 9. Индекс
### A
- `arity` - [1.8](#18-арности-и-претоварване)
- `assert` - [1.6](#16-пред--и-пост-условия)
### C
- `chunked-seq?` - [2.2](#22-chunked-серии)
- `coll` - [5.3](#53-разширяване-на-колекции)
- `complement` - [1.3](#13-аргументи-от-тип-ключова-дума)
- `comp` - [1.3](#13-аргументи-от-тип-ключова-дума)
### D
- `delay` - [2.6](#26-производителност-на-сериите)
- `delayed?` - [2.6](#26-производителност-на-сериите)
- `deref` - [2.6](#26-производителност-на-сериите)
### F
- `force` - [2.6](#26-производителност-на-сериите)
- `fnil` - [1.3](#13-аргументи-от-тип-ключова-дума)
- `fold` - [6.2](#62-използване-на-reducers)
- `fpartial` - [1.3](#13-аргументи-от-тип-ключова-дума)
### G
- `gen` - [4.5](#45-генеративно-тестване)
- `generate` - [4.5](#45-генеративно-тестване)
### I
- `into` - [3.2](#32-използване-на-трансдюсъри)
- `iterate` - [2.5](#25-безкрайни-серии)
### L
- `lazy-cat` - [2.3](#23-lazy-cons-и-реализация)
- `lazy-seq` - [2.3](#23-lazy-cons-и-реализация)
- `let` - [1.2](#12-rest-параметри-в-детайли)
### M
- `memoize` - [1.5](#15-мемоизация)
- `multi-spec` - [4.6](#46-multi-spec)
- `mmerge` - [6.1](#61-използване-на-reducers)
### N
- `nested` - [5.3](#53-разширяване-на-колекции)
- `next` - [5.2](#52-ключови-протоколи)
### P
- `parallelize` - [7.2](#72-reducers-за-паралелизъм)
- `partial` - [1.3](#13-аргументи-от-тип-ключова-дума)
- `pmap` - [7.1](#71-pmap)
- `promote` - [6.2](#62-използване-на-reducers)
### R
- `realized?` - [2.1](#21-реализиране-на-серии)
- `reduced` - [3.4](#34-ранно-прекратяване)
- `reduced?` - [3.4](#34-ранно-прекратяване)
- `reductions` - [3.3](#33-завършващи-редукции)
### S
- `sample` - [4.5](#45-генеративно-тестване)
- `sequence` - [3.2](#32-използване-на-трансдюсъри)
- `spec` - [4.1](#41-въведение-в-spec)
- `split-with` - [2.6](#26-производителност-на-сериите)
### T
- `test` - [4.5](#45-генеративно-тестване)
- `transduce` - [3.2](#32-използване-на-трансдюсъри)
- `transient` - [8.2](#82-transient-структури-от-данни)
- `tree-seq` - [2.6](#26-производителност-на-сериите)
### V
- `volatile!` - [1.7](#17-метаданни-на-функции)
- `volatile?` - [1.7](#17-метаданни-на-функции)
---
*Чист Clojure: Разширени теми*