В «Радости Clojure» (TJoC) деструктуризация описывается как мини-язык в Clojure. Не обязательно изучать этот мини-язык; однако, как отмечают авторы TJoC, деструктуризация упрощает сжатый и элегантный код.
Что такое деструкция?
Clojure поддерживает абстрактную структурную привязку, часто называемую деструктуризацией, в списках привязки let, списках параметров fn и любом макросе, который расширяется до let или fn. —
http://clojure.org/special_forms
Простейшим примером деструктуризации является присвоение значений вектора.
user=> (def point [5 7]) #'user/point user=> (let [[x y] point] (println "x:" x "y:" y)) x: 5 y: 7
примечание: я использую let для моих примеров деструктуризации; однако на практике я склонен использовать деструктуризацию в списках параметров функции по крайней мере так же часто, если не чаще.
Я признаю, что не помню, чтобы когда-либо использовался деструктурирование, как в первом примере, но это хорошая отправная точка. Более реалистичным примером является разбиение вектора на голову и хвост. При определении функции с помощью arglist ** вы используете амперсанд. То же самое относится и к разрушению.
user=> (def indexes [1 2 3]) #'user/indexes user=> (let [[x & more] indexes] (println "x:" x "more:" more)) x: 1 more: (2 3)
Также стоит отметить, что вы можете привязать весь вектор к локальному с помощью директивы: as.
user=> (def indexes [1 2 3])
#'user/indexes
user=> (let [[x & more :as full-list] indexes]
(println "x:" x "more:" more "full list:" full-list))
x: 1 more: (2 3) full list: [1 2 3]
Векторные примеры являются самыми простыми; однако на практике я гораздо чаще использую деструктуризацию с картами.
Простая деструктуризация на карте так же проста, как выбор локального имени и предоставление ключа.
user=> (def point {:x 5 :y 7}) #'user/point user=> (let [{the-x 😡 the-y :y} point] (println "x:" the-x "y:" the-y)) x: 5 y: 7
Как показывает пример, значения: x и: y привязаны к местным жителям с именами the-x и the-y. На практике мы бы никогда не добавили «-» к нашим местным именам; однако, использование разных имен обеспечивает немного ясности для нашего первого примера. В рабочем коде вы, скорее всего, захотите, чтобы местные жители с тем же именем, что и ключ. Это прекрасно работает, как показывает следующий пример.
user=> (def point {:x 5 :y 7}) #'user/point user=> (let [{x 😡 y :y} point] (println "x:" x "y:" y)) x: 5 y: 7
Хотя это прекрасно работает, создание локальных пользователей с тем же именем, что и у ключей, становится утомительным и раздражающим (особенно если ваши ключи длиннее одной буквы). Clojure предвидит это разочарование и предоставляет директиву: keys, которая позволяет вам указать ключи, которые вы хотели бы использовать в качестве локальных пользователей с тем же именем.
user=> (def point {:x 5 :y 7}) #'user/point user=> (let [{:keys [x y]} point] (println "x:" x "y:" y)) x: 5 y: 7
Есть несколько директив, которые работают при деструктурировании карт. В приведенном выше примере показано использование ключей:. На практике я использую: ключи чаще всего; однако я также использовал директиву: as при работе с картами.
В следующем примере показано использование директивы: as для привязки локального объекта ко всей карте.
user=> (def point {:x 5 :y 7}) #'user/point user=> (let [{:keys [x y] :as the-point} point] (println "x:" x "y:" y "point:" the-point)) x: 5 y: 7 point: {:x 5, :y 7}
Теперь мы видели директиву: as, используемую как для векторов, так и для карт. В обоих случаях локальное всегда присваивается всему выражению, которое подвергается деструктуризации.
Для полноты я задокументирую директиву: or; Однако я должен признать, что никогда не использовал это на практике. Директива: or используется для назначения значений по умолчанию, когда карта с деструктуризацией не содержит указанный ключ.
user=> (def point {:y 7}) #'user/point user=> (let [{:keys [x y] :or {x 0 y 0}} point] (println "x:" x "y:" y)) x: 0 y: 7
Наконец, также стоит отметить, что вы можете деструктурировать вложенные карты, векторы и их комбинацию.
Следующий пример разрушает вложенную карту
user=> (def book {:name "SICP" :details {:pages 657 :isbn-10 "0262011530"}}) #'user/book user=> (let [{name :name {pages :pages isbn-10 :isbn-10} :details} book] (println "name:" name "pages:" pages "isbn-10:" isbn-10)) name: SICP pages: 657 isbn-10: 0262011530
Как и следовало ожидать, вы также можете использовать директивы при разрушении вложенных карт.
user=> (def book {:name "SICP" :details {:pages 657 :isbn-10 "0262011530"}}) #'user/book user=> user=> (let [{name :name {:keys [pages isbn-10]} :details} book] (println "name:" name "pages:" pages "isbn-10:" isbn-10)) name: SICP pages: 657 isbn-10: 0262011530
Разрушение вложенных векторов также очень просто, как показано в следующем примере.
user=> (def numbers [[1 2][3 4]]) #'user/numbers user=> (let [[[a b][c d]] numbers] (println "a:" a "b:" b "c:" c "d:" d)) a: 1 b: 2 c: 3 d: 4
Поскольку формы связывания могут быть произвольно вложены друг в друга, вы можете разделить все что угодно —
http://clojure.org/special_forms
Следующий пример уничтожает карту и вектор одновременно.
user=> (def golfer {:name "Jim" :scores [3 5 4 5]}) #'user/golfer user=> (let [{name :name [hole1 hole2] :scores} golfer] (println "name:" name "hole1:" hole1 "hole2:" hole2)) name: Jim hole1: 3 hole2: 5
Тот же пример может быть переписан с использованием определения функции, чтобы показать простоту использования деструктуризации в списках параметров.
user=> (defn print-status [{name :name [hole1 hole2] :scores}] (println "name:" name "hole1:" hole1 "hole2:" hole2)) #'user/print-status user=> (print-status {:name "Jim" :scores [3 5 4 5]}) name: Jim hole1: 3 hole2: 5
Другие (менее используемые) директивы и более глубокие объяснения доступны на
http://clojure.org/special_forms и в
«Радости Clojure» . Я рекомендую оба.
** (определите что-то сделать [xy & more] …)
С http://blog.jayfields.com/2010/07/clojure-destructuring.html