Деструктуризация
Учимся вытаскивать значения из коллекций компактно — прямо в момент связывания.
Деструктуризация — синтаксис, позволяющий разобрать коллекцию на части и сразу присвоить им имена, не доставая элементы по одному.
Зачем это нужно
Часто из вектора или map нужно достать несколько значений. Без деструктуризации приходится писать (nth v 0), (nth v 1), (:имя m) и так далее. Деструктуризация делает это в одну строку и читается куда яснее.
Разбор вектора
Чтобы разобрать вектор, в позиции имени ставим вектор имён:
(let [[a b c] [10 20 30]]
(println a b c))Вывод:
10 20 30
Можно пропускать ненужное через _ и собрать «хвост» через &:
(let [[первый & остальные] [1 2 3 4]]
(println первый остальные))Вывод:
1 (2 3 4)
Разбор отображения (map)
Для map указываем, какие ключи достать. Удобнее всего :keys:
(def человек {:имя "Лена" :возраст 28})
(let [{:keys [имя возраст]} человек]
(println имя возраст))Вывод:
Лена 28
Дополнительные приёмы: :as сохраняет весь map целиком, :or задаёт значения по умолчанию для отсутствующих ключей:
(let [{:keys [имя город] :or {город "неизвестен"} :as всё}
{:имя "Лена"}]
(println имя город))Вывод:
Лена неизвестен
Деструктуризация в параметрах функций
Самое полезное — разбирать аргумент прямо в сигнатуре функции:
(defn приветствие [{:keys [имя возраст]}]
(str "Привет, " имя ", тебе " возраст))
(приветствие {:имя "Макс" :возраст 40})Вывод:
"Привет, Макс, тебе 40"
Как работает под капотом
Деструктуризация — это синтаксический сахар. Компилятор раскрывает её в обычные вызовы nth, get и связывания через let. То есть {:keys [имя]} превращается во что-то вроде имя (get m :имя). Никакой магии в рантайме — всё разворачивается на этапе компиляции, поэтому деструктуризация ничего не стоит по скорости сверх обычного доступа.
Частые ошибки
- Путать
:keysи значения. В:keysперечисляют ключи без двоеточия::keys [имя], а получают значения по ключу:имя. - Забывать
:orдля необязательных полей. Без него отсутствующий ключ дастnil. - Слишком глубокая деструктуризация. Разбор на пять уровней вглубь читается хуже, чем явные
get-in; знайте меру.
Итоги
- Деструктуризация разбирает коллекцию и сразу даёт имена частям.
- Векторы разбираются позиционно (
[a b],&для хвоста), map — по ключам (:keys). :asсохраняет всю коллекцию,:orзадаёт значения по умолчанию.- Особенно удобна в параметрах функций; это синтаксический сахар над get/nth.