Skip to content

Instantly share code, notes, and snippets.

@dmitry-osin
Created April 18, 2025 10:57
Show Gist options
  • Save dmitry-osin/882f8b2eefb641647e7aec0b55b83548 to your computer and use it in GitHub Desktop.
Save dmitry-osin/882f8b2eefb641647e7aec0b55b83548 to your computer and use it in GitHub Desktop.
Шпаргалка по Clojure

Полная шпаргалка по языку программирования Clojure

Основы Clojure

Что такое Clojure?

Clojure — это современный диалект языка Lisp, работающий на платформе JVM. Это функциональный язык с акцентом на иммутабельность данных и параллельное программирование.

Основной синтаксис

;; Это комментарий

;; Определение функции
(defn hello [name]
  (println "Привет," name))

;; Вызов функции
(hello "мир")

Типы данных

Скалярные типы

;; Числа
42          ;; целое число
3.14        ;; число с плавающей точкой
22/7        ;; рациональное число
1N          ;; BigInt
1.0M        ;; BigDecimal

;; Строки
"Привет, мир"

;; Символы
'symbol
:keyword    ;; ключевое слово (часто используется как ключи в словарях)

;; Булевы значения
true
false
nil         ;; эквивалент null

Коллекции

;; Списки (связанные списки)
'(1 2 3 4)
(list 1 2 3 4)

;; Векторы (массивы)
[1 2 3 4]
(vector 1 2 3 4)

;; Словари (ассоциативные массивы)
{:name "Иван" :age 30}
(hash-map :name "Иван" :age 30)

;; Множества
#{1 2 3 4}
(hash-set 1 2 3 4)

Функции и управляющие структуры

Определение функций

;; Простая функция
(defn square [x]
  (* x x))

;; Функция с несколькими аргументами
(defn add [a b]
  (+ a b))

;; Функция с переменным числом аргументов
(defn sum [& numbers]
  (apply + numbers))

;; Анонимная функция
(fn [x] (* x x))
#(* % %)  ;; краткая форма

Условные выражения

;; if
(if (> x 0)
  "Положительное"
  "Отрицательное или ноль")

;; when (if без else)
(when (> x 0)
  (println "x положительное")
  x)

;; cond (множественные условия)
(cond
  (< x 0) "Отрицательное"
  (> x 0) "Положительное"
  :else "Ноль")

;; case (аналог switch)
(case day
  "Понедельник" "Начало недели"
  "Пятница" "Конец рабочей недели"
  "Другой день")

Циклы и итерации

;; doseq (для побочных эффектов)
(doseq [x [1 2 3]]
  (println x))

;; for (для создания последовательностей)
(for [x [1 2 3]
      y [4 5 6]]
  [x y])

;; loop/recur (хвостовая рекурсия)
(loop [x 10]
  (when (> x 0)
    (println x)
    (recur (dec x))))

Thread-first и Thread-last макросы

Thread-first (->) и Thread-last (->>) макросы позволяют писать более читаемый код, избегая глубокой вложенности выражений.

Thread-first макрос (->)

Thread-first макрос вставляет каждое выражение как первый аргумент следующей формы.

;; Без thread-first
(+ (* (- 10 3) 2) 5)  ;; => 19

;; С thread-first
(-> 10
    (- 3)
    (* 2)
    (+ 5))  ;; => 19

;; Эквивалентно:
;; (+ (* (- 10 3) 2) 5)

;; Пример с функциями для работы со словарями
(-> {:name "Иван" :age 30}
    (assoc :city "Москва")
    (update :age inc)
    (dissoc :name))  ;; => {:age 31 :city "Москва"}

;; Эквивалентно:
;; (dissoc (update (assoc {:name "Иван" :age 30} :city "Москва") :age inc) :name)

Thread-last макрос (->>)

Thread-last макрос вставляет каждое выражение как последний аргумент следующей формы.

;; Без thread-last
(reduce + (filter even? (map #(* % %) (range 10))))  ;; => 120

;; С thread-last
(->> (range 10)
     (map #(* % %))
     (filter even?)
     (reduce +))  ;; => 120

;; Эквивалентно:
;; (reduce + (filter even? (map #(* % %) (range 10))))

;; Пример обработки данных
(->> [{:name "Иван" :age 30}
      {:name "Мария" :age 25}
      {:name "Алексей" :age 40}]
     (filter #(> (:age %) 25))
     (map :name)
     (clojure.string/join ", "))  ;; => "Иван, Алексей"

Комбинирование thread-first и thread-last

;; Комбинирование -> и ->>
(-> {:users [{:name "Иван" :age 30}
             {:name "Мария" :age 25}
             {:name "Алексей" :age 40}]}
    (update :users 
            #(->> %
                  (filter (fn [user] (> (:age user) 25)))
                  (map :name))))  ;; => {:users ("Иван" "Алексей")}

Другие threading макросы

;; as-> (для произвольного позиционирования аргумента)
(as-> (range 10) x
      (map #(* % %) x)
      (filter even? x)
      (reduce + x)
      (str "Сумма квадратов чётных чисел: " x))
;; => "Сумма квадратов чётных чисел: 120"

;; some-> (короткое замыкание при nil)
(some-> {:user {:name "Иван"}}
        :user
        :age
        inc)  ;; => nil (т.к. :age отсутствует)

;; cond-> (условное применение функций)
(cond-> 10
        true (+ 5)
        false (* 2)
        (> 10 5) (- 3))  ;; => 12

Работа с коллекциями

Основные функции

;; map (применяет функцию к каждому элементу)
(map inc [1 2 3 4])  ;; => (2 3 4 5)

;; filter (отбирает элементы по условию)
(filter even? [1 2 3 4])  ;; => (2 4)

;; reduce (сворачивает коллекцию)
(reduce + [1 2 3 4])  ;; => 10

;; first, rest, last
(first [1 2 3])  ;; => 1
(rest [1 2 3])   ;; => (2 3)
(last [1 2 3])   ;; => 3

;; conj (добавляет элементы)
(conj [1 2] 3)   ;; => [1 2 3]
(conj '(2 3) 1)  ;; => (1 2 3)

Функции для словарей

;; get
(get {:a 1 :b 2} :a)  ;; => 1
(get {:a 1 :b 2} :c)  ;; => nil
(get {:a 1 :b 2} :c 0)  ;; => 0 (с дефолтным значением)

;; assoc (добавляет или обновляет пары ключ-значение)
(assoc {:a 1} :b 2)  ;; => {:a 1 :b 2}

;; dissoc (удаляет пары ключ-значение)
(dissoc {:a 1 :b 2} :a)  ;; => {:b 2}

;; keys, vals
(keys {:a 1 :b 2})  ;; => (:a :b)
(vals {:a 1 :b 2})  ;; => (1 2)

Деструктуризация

Деструктуризация - мощный механизм для извлечения значений из структур данных.

Деструктуризация векторов

;; Базовая деструктуризация вектора
(let [[a b c] [1 2 3]]
  (println a b c))  ;; => 1 2 3

;; С остаточным параметром
(let [[first second & rest] [1 2 3 4 5]]
  (println first)  ;; => 1
  (println second)  ;; => 2
  (println rest))  ;; => (3 4 5)

;; Пропуск элементов
(let [[a _ c] [1 2 3]]
  (println a c))  ;; => 1 3

;; Вложенная деструктуризация
(let [[[a b] [c d]] [[1 2] [3 4]]]
  (println a b c d))  ;; => 1 2 3 4

Деструктуризация словарей

;; Базовая деструктуризация словаря
(let [{name :name age :age} {:name "Иван" :age 30}]
  (println name age))  ;; => Иван 30

;; Использование :keys
(let [{:keys [name age]} {:name "Иван" :age 30}]
  (println name age))  ;; => Иван 30

;; Использование :strs (для строковых ключей)
(let [{:strs [name age]} {"name" "Иван" "age" 30}]
  (println name age))  ;; => Иван 30

;; Использование :syms (для символьных ключей)
(let [{:syms [name age]} {'name "Иван" 'age 30}]
  (println name age))  ;; => Иван 30

;; Значения по умолчанию с :or
(let [{:keys [name age] :or {age 25}} {:name "Иван"}]
  (println name age))  ;; => Иван 25

;; Связывание всего словаря с :as
(let [{:keys [name] :as person} {:name "Иван" :age 30}]
  (println name)
  (println person))  ;; => Иван {:name "Иван" :age 30}

Деструктуризация в аргументах функций

;; Деструктуризация вектора
(defn process-point [[x y]]
  (println "Координаты:" x y))

(process-point [10 20])  ;; => Координаты: 10 20

;; Деструктуризация словаря
(defn greet-person [{:keys [name age]}]
  (println "Привет," name "! Тебе" age "лет."))

(greet-person {:name "Иван" :age 30})  ;; => Привет, Иван! Тебе 30 лет.

Ключевые слова (Keywords)

Ключевые слова - это специальные символы, начинающиеся с двоеточия. Они самоэвалуируются и часто используются как ключи в словарях.

;; Определение ключевых слов
:name
:age
:user/name  ;; с пространством имён

;; Использование как функций для получения значений из словарей
(:name {:name "Иван" :age 30})  ;; => "Иван"
(:address {:name "Иван"} "Не указан")  ;; => "Не указан" (со значением по умолчанию)

;; Сравнение с использованием get
(get {:name "Иван" :age 30} :name)  ;; => "Иван"

;; Ключевые слова в функциях
(defn configure [options]
  (let [debug? (:debug options false)
        level (:level options :info)]
    (println "Debug:" debug? "Level:" level)))

(configure {:debug true})  ;; => Debug: true Level: :info

Пространства имён и импорты

;; Определение пространства имён
(ns my-app.core)

;; Импорт Clojure библиотек
(ns my-app.core
  (:require [clojure.string :as str]))

;; Импорт Java классов
(ns my-app.core
  (:import [java.util Date]))

;; Использование
(str/upper-case "hello")  ;; => "HELLO"
(Date.)  ;; создаёт новый экземпляр Date

Управление состоянием

Atom (атомы)

Атомы обеспечивают атомарное обновление значений без необходимости координации.

;; Создание атома
(def counter (atom 0))

;; Получение значения (разыменование)
@counter  ;; => 0

;; Установка нового значения
(reset! counter 100)  ;; => 100

;; Атомарное обновление
(swap! counter inc)  ;; => 101
(swap! counter + 10)  ;; => 111

;; Условное обновление
(swap! counter (fn [current]
                 (if (> current 200)
                   current
                   (+ current 50))))

;; Наблюдатели (watches)
(add-watch counter :logger
  (fn [key atom old-state new-state]
    (println "Значение изменилось с" old-state "на" new-state)))

;; Валидаторы
(def positive (atom 1 :validator pos?))
(reset! positive 5)  ;; работает
;; (reset! positive -5)  ;; выбросит исключение

Ref (ссылки)

Refs используются для координированных изменений нескольких ссылок внутри транзакций.

;; Создание ссылок
(def account1 (ref 1000))
(def account2 (ref 500))

;; Чтение значений
@account1  ;; => 1000
@account2  ;; => 500

;; Транзакция с изменением нескольких ссылок
(dosync
  (alter account1 - 200)
  (alter account2 + 200))

;; После транзакции
@account1  ;; => 800
@account2  ;; => 700

;; Использование commute (для коммутативных операций)
(dosync
  (commute account1 + 100)
  (commute account2 + 50))

;; Использование ensure для блокировки без изменения
(dosync
  (ensure account1)  ;; блокирует, но не меняет
  (when (> @account1 500)
    (alter account2 + 100)))

;; Валидаторы
(def balance (ref 1000 :validator #(>= % 0)))
;; (dosync (alter balance - 2000))  ;; выбросит исключение

Agent (агенты)

Агенты обеспечивают асинхронное изменение состояния с гарантией сериализации действий.

;; Создание агента
(def counter (agent 0))

;; Отправка действий (асинхронно)
(send counter inc)
(send counter + 10)

;; Блокирующее ожидание завершения всех действий
(await counter)
@counter  ;; => 11

;; Отправка действий, которые могут выбросить исключения
(send-off counter (fn [_] (Thread/sleep 1000) 42))

;; Обработка ошибок
(agent-error counter)  ;; возвращает исключение или nil
(restart-agent counter 0)  ;; перезапускает агент с новым значением

;; Режим обработки ошибок
(set-error-handler! counter (fn [agent exception]
                             (println "Ошибка:" exception)))
(set-error-mode! counter :continue)  ;; продолжать работу при ошибках

Var (переменные)

Vars - это ссылки на значения в пространстве имён.

;; Определение var
(def x 10)

;; Динамические vars
(def ^:dynamic *debug* false)

;; Временное изменение значения динамической переменной
(binding [*debug* true]
  (println "Debug mode:" *debug*))

;; После binding значение возвращается к исходному
*debug*  ;; => false

;; Потокобезопасные изменения
(alter-var-root #'x inc)  ;; => 11

;; Приватные vars
(def ^:private secret "секретное значение")

Параллельное программирование

;; Атомы (атомарные ссылки)
(def counter (atom 0))
(swap! counter inc)  ;; атомарно увеличивает значение
(reset! counter 0)   ;; устанавливает новое значение
@counter             ;; разыменовывает атом

;; Агенты (асинхронные ссылки)
(def scores (agent {}))
(send scores assoc :player1 100)

;; Refs (для координированных транзакций)
(def account1 (ref 1000))
(def account2 (ref 500))
(dosync
  (alter account1 - 100)
  (alter account2 + 100))

Макросы

;; Определение макроса
(defmacro when-positive [test & body]
  `(when (pos? ~test)
     ~@body))

;; Использование макроса
(when-positive 5
  (println "Положительное число")
  (println "Выполняем код"))

;; Макрос для отладки
(defmacro dbg [x]
  `(let [x# ~x]
     (println "debug:" '~x "=" x#)
     x#))

Обработка исключений

;; try/catch/finally
(try
  (/ 1 0)
  (catch ArithmeticException e
    (println "Ошибка:" (.getMessage e)))
  (finally
    (println "Выполняется всегда")))

;; throw
(throw (Exception. "Что-то пошло не так"))

Интероперабельность с Java

;; Вызов статических методов
(Math/sqrt 16)  ;; => 4.0

;; Создание объектов
(new java.util.Date)
(java.util.Date.)  ;; эквивалентно

;; Вызов методов
(.toUpperCase "hello")  ;; => "HELLO"

;; Доступ к полям
(.-x point)  ;; доступ к полю x

Transient коллекции

Transient коллекции используются для эффективного построения больших коллекций.

;; Создание transient коллекции
(def v (transient []))

;; Добавление элементов
(def v (conj! v 1))
(def v (conj! v 2))
(def v (conj! v 3))

;; Преобразование обратно в персистентную коллекцию
(persistent! v)  ;; => [1 2 3]

;; Пример с reduce
(persistent!
  (reduce conj! (transient []) (range 1000)))

Метаданные

Метаданные позволяют присоединять дополнительную информацию к символам и коллекциям.

;; Добавление метаданных
(def ^{:doc "Счётчик посещений"} counter (atom 0))

;; Получение метаданных
(meta #'counter)  ;; => {:doc "Счётчик посещений"}

;; Метаданные в определениях функций
(defn ^:private square
  "Возводит число в квадрат"
  [x]
  (* x x))

;; Метаданные для типизации
(defn add
  ^Long [^Long x ^Long y]
  (+ x y))

Эта шпаргалка охватывает основные аспекты языка Clojure, включая синтаксис, типы данных, функции, коллекции, деструктуризацию, ключевые слова, thread-first и thread-last макросы, пространства имён, механизмы управления состоянием (atom, ref, agent, var), макросы, обработку исключений, интероперабельность с Java, transient коллекции и метаданные.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment