clojure
Коллекции и последовательности
Поиск…
Синтаксис
-
'()
→()
-
'(1 2 3 4 5)
→(1 2 3 4 5)
-
'(1 foo 2 bar 3)
→(1 'foo 2 'bar 3)
-
(
list
1 2 3 4 5)
→(1 2 3 4 5)
-
(
list*
[1 2 3 4 5])
→(1 2 3 4 5)
-
[]
→[]
-
[1 2 3 4 5]
→[1 2 3 4 5]
-
(
vector
1 2 3 4 5)
→[1 2 3 4 5]
-
(
vec
'(1 2 3 4 5))
→[1 2 3 4 5]
-
{}
=>{}
-
{:keyA 1 :keyB 2}
→{:keyA 1 :keyB 2}
-
{:keyA 1, :keyB 2}
→{:keyA 1 :keyB 2}
-
(
hash-map
:keyA 1 :keyB 2)
→{:keyA 1 :keyB 2}
-
(
sorted-map
5 "five" 1 "one")
→{1 "one" 5 "five"}
(записи сортируются по ключу при использовании в качестве последовательности) -
#{}
→#{}
-
#{1 2 3 4 5}
→#{4 3 2 5 1}
(неупорядоченный) -
(
hash-set
1 2 3 4 5)
→#{2 5 4 1 3}
(неупорядоченный) -
(
sorted-set
2 5 4 3 1)
→#{1 2 3 4 5}
Коллекции
Все встроенные коллекции Clojure неизменяемы и неоднородны, имеют буквальный синтаксис и поддерживают функции conj
, count
и seq
.
-
conj
возвращает новую коллекцию, которая эквивалентна существующей коллекции с добавленным элементом в «постоянном» или логарифмическом времени. Что именно это означает, зависит от коллекции. -
count
возвращает количество элементов в коллекции в постоянное время. -
seq
возвращаетnil
для пустой коллекции или последовательности элементов для непустой коллекции в постоянное время.
Списки
Список обозначается круглыми скобками:
()
;;=> ()
Список Clojure - это отдельный список . conj
«соединяет» новый элемент коллекции в наиболее эффективном месте. Для списков это в начале:
(conj () :foo)
;;=> (:foo)
(conj (conj () :bar) :foo)
;;=> (:foo :bar)
В отличие от других коллекций, непустые списки оцениваются как вызовы специальных форм, макросов или функций при оценке. Поэтому while (:foo)
является литеральным представлением списка, содержащего :foo
как единственный элемент, вычисление (:foo)
в REPL приведет к IllegalArgumentException
, потому что ключевое слово не может быть вызвано как функция с нулевым значением .
(:foo)
;; java.lang.IllegalArgumentException: Wrong number of args passed to keyword: :foo
Чтобы Clojure не оценивал непустой список, вы можете quote
его:
'(:foo)
;;=> (:foo)
'(:foo :bar)
;;=> (:foo :bar)
К сожалению, это не позволяет оценить элементы:
(+ 1 1)
;;=> 2
'(1 (+ 1 1) 3)
;;=> (1 (+ 1 1) 3)
По этой причине вы обычно хотите использовать list
, вариационную функцию, которая оценивает все свои аргументы и использует эти результаты для построения списка:
(list)
;;=> ()
(list :foo)
;;=> (:foo)
(list :foo :bar)
;;=> (:foo :bar)
(list 1 (+ 1 1) 3)
;;=> (1 2 3)
count
возвращает количество элементов в постоянное время:
(count ())
;;=> 0
(count (conj () :foo))
;;=> 1
(count '(:foo :bar))
;;=> 2
Вы можете проверить, есть ли список, который используется в list?
сказуемое:
(list? ())
;;=> true
(list? '(:foo :bar))
;;=> true
(list? nil)
;;=> false
(list? 42)
;;=> false
(list? :foo)
;;=> false
Вы можете получить первый элемент списка, используя peek
:
(peek ())
;;=> nil
(peek '(:foo))
;;=> :foo
(peek '(:foo :bar))
;;=> :foo
Вы можете получить новый список без первого элемента, используя pop
:
(pop '(:foo))
;;=> ()
(pop '(:foo :bar))
;;=> (:bar)
Обратите внимание , что если вы пытаетесь pop
пустой список, вы получите IllegalStateException
:
(pop ())
;; java.lang.IllegalStateException: Can't pop empty list
Наконец, все списки являются последовательностями, поэтому вы можете сделать все со списком, который вы можете сделать с любой другой последовательностью. Действительно, за исключением пустого списка, вызов seq
в списке возвращает тот же самый объект:
(seq ())
;;=> nil
(seq '(:foo))
;;=> (:foo)
(seq '(:foo :bar))
;;=> (:foo :bar)
(let [x '(:foo :bar)]
(identical? x (seq x)))
;;=> true
Последовательности
Последовательность очень похожа на список: это неизменный объект, который может дать вам свой first
элемент или rest
его элементы в постоянное время. Вы можете также cons
truct новой последовательности из существующей последовательности и элемента придерживаться в начале.
Вы можете проверить, есть ли какая-то последовательность с помощью seq?
сказуемое:
(seq? nil)
;;=> false
(seq? 42)
;;=> false
(seq? :foo)
;;=> false
Как вы уже знаете, списки - это последовательности:
(seq? ())
;;=> true
(seq? '(:foo :bar))
;;=> true
Все, что вы получаете, вызывая seq
или rseq
или keys
или vals
в непустой коллекции, также является последовательностью:
(seq? (seq ()))
;;=> false
(seq? (seq '(:foo :bar)))
;;=> true
(seq? (seq []))
;;=> false
(seq? (seq [:foo :bar]))
;;=> true
(seq? (rseq []))
;;=> false
(seq? (rseq [:foo :bar]))
;;=> true
(seq? (seq {}))
;;=> false
(seq? (seq {:foo :bar :baz :qux}))
;;=> true
(seq? (keys {}))
;;=> false
(seq? (keys {:foo :bar :baz :qux}))
;;=> true
(seq? (vals {}))
;;=> false
(seq? (vals {:foo :bar :baz :qux}))
;;=> true
(seq? (seq #{}))
;;=> false
(seq? (seq #{:foo :bar}))
;;=> true
Помните, что все списки - это последовательности, но не все последовательности - это списки. В то время как списки поддерживают peek
и pop
и count
в постоянное время, в целом, последовательность не нуждается в поддержке какой-либо из этих функций. Если вы попытаетесь вызвать peek
или pop
в последовательности, которая также не поддерживает интерфейс стека Clojure, вы получите ClassCastException
:
(peek (seq [:foo :bar]))
;; java.lang.ClassCastException: clojure.lang.PersistentVector$ChunkedSeq cannot be cast to clojure.lang.IPersistentStack
(pop (seq [:foo :bar]))
;; java.lang.ClassCastException: clojure.lang.PersistentVector$ChunkedSeq cannot be cast to clojure.lang.IPersistentStack
(peek (seq #{:foo :bar}))
;; java.lang.ClassCastException: clojure.lang.APersistentMap$KeySeq cannot be cast to clojure.lang.IPersistentStack
(pop (seq #{:foo :bar}))
;; java.lang.ClassCastException: clojure.lang.APersistentMap$KeySeq cannot be cast to clojure.lang.IPersistentStack
(peek (seq {:foo :bar :baz :qux}))
;; java.lang.ClassCastException: clojure.lang.PersistentArrayMap$Seq cannot be cast to clojure.lang.IPersistentStack
(pop (seq {:foo :bar :baz :qux}))
;; java.lang.ClassCastException: clojure.lang.PersistentArrayMap$Seq cannot be cast to clojure.lang.IPersistentStack
Если вы вызываете count
в последовательности, которая не использует count
в постоянное время, вы не получите ошибку; вместо этого Clojure будет пересекать всю последовательность до тех пор, пока она не достигнет конца, а затем вернет количество перемещаемых элементов. Это означает, что для общих последовательностей count
является линейным, а не постоянным временем. Вы можете проверить, поддерживает ли что-либо значение count
постоянного времени с помощью counted?
сказуемое:
(counted? '(:foo :bar))
;;=> true
(counted? (seq '(:foo :bar)))
;;=> true
(counted? [:foo :bar])
;;=> true
(counted? (seq [:foo :bar]))
;;=> true
(counted? {:foo :bar :baz :qux})
;;=> true
(counted? (seq {:foo :bar :baz :qux}))
;;=> true
(counted? #{:foo :bar})
;;=> true
(counted? (seq #{:foo :bar}))
;;=> false
Как упоминалось выше, вы можете first
использовать первый элемент последовательности. Обратите внимание, что first
вызовет seq
по их аргументу, поэтому его можно использовать во всех «seqable», а не только в реальных последовательностях:
(first nil)
;;=> nil
(first '(:foo))
;;=> :foo
(first '(:foo :bar))
;;=> :foo
(first [:foo])
;;=> :foo
(first [:foo :bar])
;;=> :foo
(first {:foo :bar})
;;=> [:foo :bar]
(first #{:foo})
;;=> :foo
Также, как упоминалось выше, вы можете использовать rest
чтобы получить последовательность, содержащую все, кроме первого элемента существующей последовательности. Как и first
, он вызывает seq
на его аргумент. Однако он не вызывает seq
по его результату! Это означает, что если вы назовете rest
последовательности, содержащей менее двух элементов, вы вернетесь ()
вместо nil
:
(rest nil)
;;=> ()
(rest '(:foo))
;;=> ()
(rest '(:foo :bar))
;;=> (:bar)
(rest [:foo])
;;=> ()
(rest [:foo :bar])
;;=> (:bar)
(rest {:foo :bar})
;;=> ()
(rest #{:foo})
;;=> ()
Если вы хотите вернуться в nil
когда в последовательности больше нет элементов, вы можете использовать next
а не rest
:
(next nil)
;;=> nil
(next '(:foo))
;;=> nil
(next [:foo])
;;=> nil
Вы можете использовать функцию cons
для создания новой последовательности, которая вернет свой первый аргумент для first
и второго аргумента для rest
:
(cons :foo nil)
;;=> (:foo)
(cons :foo (cons :bar nil))
;;=> (:foo :bar)
Clojure предоставляет библиотеку большой последовательности с множеством функций для обработки последовательностей. Важная вещь в этой библиотеке заключается в том, что она работает с чем-то «seqable», а не только с списками. Вот почему концепция последовательности настолько полезна; это означает, что одна функция, например reduce
, отлично работает в любой коллекции:
(reduce + '(1 2 3))
;;=> 6
(reduce + [1 2 3])
;;=> 6
(reduce + #{1 2 3})
;;=> 6
Другая причина, по которой эти последовательности полезны, заключается в том, что, поскольку они не предусматривают какой-либо конкретной реализации first
и rest
, они допускают ленивые последовательности, элементы которых реализуются только тогда, когда это необходимо.
Учитывая выражение, которое создало бы последовательность, вы можете обернуть это выражение в макросе lazy-seq
чтобы получить объект, который действует как последовательность, но будет фактически оценивать это выражение только тогда, когда его попросят выполнить функцию seq
, в что указывает на то, что он будет кэшировать результат выражения и переадресовывать first
и rest
вызовы к кешированному результату.
Для конечных последовательностей ленивая последовательность обычно действует так же, как эквивалентная последовательность:
(seq [:foo :bar])
;;=> (:foo :bar)
(lazy-seq [:foo :bar])
;;=> (:foo :bar)
Однако различие становится очевидным для бесконечных последовательностей:
(defn eager-fibonacci [a b]
(cons a (eager-fibonacci b (+' a b))))
(defn lazy-fibonacci [a b]
(lazy-seq (cons a (lazy-fibonacci b (+' a b)))))
(take 10 (eager-fibonacci 0 1))
;; java.lang.StackOverflowError:
(take 10 (lazy-fibonacci 0 1))
;;=> (0 1 1 2 3 5 8 13 21 34)
векторы
Вектор обозначается квадратными скобками:
[]
;;=> []
[:foo]
;;=> [:foo]
[:foo :bar]
;;=> [:foo :bar]
[1 (+ 1 1) 3]
;;=> [1 2 3]
Кроме того, используя литеральный синтаксис, вы также можете использовать vector
функцию для построения вектора:
(vector)
;;=> []
(vector :foo)
;;=> [:foo]
(vector :foo :bar)
;;=> [:foo :bar]
(vector 1 (+ 1 1) 3)
;;=> [1 2 3]
Вы можете проверить, является ли что-то вектором, использующим vector?
сказуемое:
(vector? [])
;;=> true
(vector? [:foo :bar])
;;=> true
(vector? nil)
;;=> false
(vector? 42)
;;=> false
(vector? :foo)
;;=> false
conj
добавляет элементы в конец вектора:
(conj [] :foo)
;;=> [:foo]
(conj (conj [] :foo) :bar)
;;=> [:foo :bar]
(conj [] :foo :bar)
;;=> [:foo :bar]
count
возвращает количество элементов в постоянное время:
(count [])
;;=> 0
(count (conj [] :foo))
;;=> 1
(count [:foo :bar])
;;=> 2
Вы можете получить последний элемент вектора, используя peek
:
(peek [])
;;=> nil
(peek [:foo])
;;=> :foo
(peek [:foo :bar])
;;=> :bar
Вы можете получить новый вектор без последнего элемента, используя pop
:
(pop [:foo])
;;=> []
(pop [:foo :bar])
;;=> [:foo]
Обратите внимание: если вы попытаетесь IllegalStateException
пустой вектор, вы получите IllegalStateException
:
(pop [])
;; java.lang.IllegalStateException: Can't pop empty vector
В отличие от списков, векторы индексируются. Вы можете получить элемент вектора по индексу в «постоянное» время, используя get
:
(get [:foo :bar] 0)
;;=> :foo
(get [:foo :bar] 1)
;;=> :bar
(get [:foo :bar] -1)
;;=> nil
(get [:foo :bar] 2)
;;=> nil
Кроме того, сами векторы являются функциями, которые принимают индекс и возвращают элемент в этот индекс:
([:foo :bar] 0)
;;=> :foo
([:foo :bar] 1)
;;=> :bar
Однако, если вы вызываете вектор с недопустимым индексом, вы получите IndexOutOfBoundsException
вместо nil
:
([:foo :bar] -1)
;; java.lang.IndexOutOfBoundsException:
([:foo :bar] 2)
;; java.lang.IndexOutOfBoundsException:
Вы можете получить новый вектор с другим значением в определенном индексе с помощью assoc
:
(assoc [:foo :bar] 0 42)
;;=> [42 :bar]
(assoc [:foo :bar] 1 42)
;;=> [:foo 42]
Если вы передадите индекс, равный count
вектора, Clojure добавит элемент так, как если бы вы использовали conj
. Однако, если вы передадите индекс, который является отрицательным или больше, чем count
, вы получите IndexOutOfBoundsException
:
(assoc [:foo :bar] 2 42)
;;=> [:foo :bar 42]
(assoc [:foo :bar] -1 42)
;; java.lang.IndexOutOfBoundsException:
(assoc [:foo :bar] 3 42)
;; java.lang.IndexOutOfBoundsException:
Вы можете получить последовательность элементов в векторе, используя seq
:
(seq [])
;;=> nil
(seq [:foo])
;;=> (:foo)
(seq [:foo :bar])
;;=> (:foo :bar)
Поскольку векторы индексируются, вы также можете получить обратную последовательность элементов вектора, используя rseq
:
(rseq [])
;;=> nil
(rseq [:foo])
;;=> (:foo)
(rseq [:foo :bar])
;;=> (:bar :foo)
Обратите внимание, что, хотя все списки являются последовательностями, а последовательности отображаются так же, как списки, а не все последовательности - это списки!
'(:foo :bar)
;;=> (:foo :bar)
(seq [:foo :bar])
;;=> (:foo :bar)
(list? '(:foo :bar))
;;=> true
(list? (seq [:foo :bar]))
;;=> false
(list? (rseq [:foo :bar]))
;;=> false
наборы
Подобно картам, наборы являются ассоциативными и неупорядоченными. В отличие от карт, которые содержат сопоставления от ключей к значениям, наборы по существу сопоставляются с ключами на себя.
Набор обозначается фигурными скобками, которым предшествует октоторп:
#{}
;;=> #{}
#{:foo}
;;=> #{:foo}
#{:foo :bar}
;;=> #{:bar :foo}
Как и в случае с картами, порядок, в котором элементы появляются в литеральном наборе, не имеет значения:
(= #{:foo :bar} #{:bar :foo})
;;=> true
Вы можете проверить, что-то задано с помощью set?
сказуемое:
(set? #{})
;;=> true
(set? #{:foo})
;;=> true
(set? #{:foo :bar})
;;=> true
(set? nil)
;;=> false
(set? 42)
;;=> false
(set? :foo)
;;=> false
Вы можете проверить, содержит ли карта данный элемент в «постоянном» времени с помощью contains?
сказуемое:
(contains? #{} :foo)
;;=> false
(contains? #{:foo} :foo)
;;=> true
(contains? #{:foo} :bar)
;;=> false
(contains? #{} nil)
;;=> false
(contains? #{nil} nil)
;;=> true
Кроме того, сами наборы являются функциями, которые берут элемент и возвращают этот элемент, если он присутствует в наборе, или nil
если это не так:
(#{} :foo)
;;=> nil
(#{:foo} :foo)
;;=> :foo
(#{:foo} :bar)
;;=> nil
(#{} nil)
;;=> nil
(#{nil} nil)
;;=> nil
Вы можете использовать conj
для получения набора, который содержит все элементы существующего набора, плюс один дополнительный элемент:
(conj #{} :foo)
;;=> #{:foo}
(conj (conj #{} :foo) :bar)
;;=> #{:bar :foo}
(conj #{:foo} :foo)
;;=> #{:foo}
Вы можете использовать disj
для получения набора, который имеет все элементы существующего набора, минус один элемент:
(disj #{} :foo)
;;=> #{}
(disj #{:foo} :foo)
;;=> #{}
(disj #{:foo} :bar)
;;=> #{:foo}
(disj #{:foo :bar} :foo)
;;=> #{:bar}
(disj #{:foo :bar} :bar)
;;=> #{:foo}
count
возвращает количество элементов в постоянное время:
(count #{})
;;=> 0
(count (conj #{} :foo))
;;=> 1
(count #{:foo :bar})
;;=> 2
Вы можете получить последовательность всех элементов в наборе с помощью seq
:
(seq #{})
;;=> nil
(seq #{:foo})
;;=> (:foo)
(seq #{:foo :bar})
;;=> (:bar :foo)
Карты
В отличие от списка, который является последовательной структурой данных, и вектор, который является как последовательным, так и ассоциативным, карта является исключительно ассоциативной структурой данных. Карта состоит из набора отображений от ключей к значениям. Все ключи уникальны, поэтому карты поддерживают «постоянный» поиск времени с клавиш на значения.
Карту обозначают фигурные скобки:
{}
;;=> {}
{:foo :bar}
;;=> {:foo :bar}
{:foo :bar :baz :qux}
;;=> {:foo :bar, :baz :qux}
Каждая пара двух элементов является парой ключ-значение. Так, например, первая карта выше не имеет отображений. Второй имеет одно отображение: от ключа :foo
до значения :bar
. Третий имеет два сопоставления: один от ключа :foo
до значения :bar
и один от ключа :baz
до значения :qux
. Карты по своей сути неупорядочены, поэтому порядок, в котором отображаются отображения, не имеет значения:
(= {:foo :bar :baz :qux}
{:baz :qux :foo :bar})
;;=> true
Вы можете проверить, есть ли карта с map?
сказуемое:
(map? {})
;;=> true
(map? {:foo :bar})
;;=> true
(map? {:foo :bar :baz :qux})
;;=> true
(map? nil)
;;=> false
(map? 42)
;;=> false
(map? :foo)
;;=> false
Вы можете проверить, содержит ли карта данный ключ в «постоянном» времени с помощью contains?
сказуемое:
(contains? {:foo :bar :baz :qux} 42)
;;=> false
(contains? {:foo :bar :baz :qux} :foo)
;;=> true
(contains? {:foo :bar :baz :qux} :bar)
;;=> false
(contains? {:foo :bar :baz :qux} :baz)
;;=> true
(contains? {:foo :bar :baz :qux} :qux)
;;=> false
(contains? {:foo nil} :foo)
;;=> true
(contains? {:foo nil} :bar)
;;=> false
Вы можете получить значение, связанное с ключом, используя get
:
(get {:foo :bar :baz :qux} 42)
;;=> nil
(get {:foo :bar :baz :qux} :foo)
;;=> :bar
(get {:foo :bar :baz :qux} :bar)
;;=> nil
(get {:foo :bar :baz :qux} :baz)
;;=> :qux
(get {:foo :bar :baz :qux} :qux)
;;=> nil
(get {:foo nil} :foo)
;;=> nil
(get {:foo nil} :bar)
;;=> nil
Кроме того, сами карты - это функции, которые берут ключ и возвращают значение, связанное с этим ключом:
({:foo :bar :baz :qux} 42)
;;=> nil
({:foo :bar :baz :qux} :foo)
;;=> :bar
({:foo :bar :baz :qux} :bar)
;;=> nil
({:foo :bar :baz :qux} :baz)
;;=> :qux
({:foo :bar :baz :qux} :qux)
;;=> nil
({:foo nil} :foo)
;;=> nil
({:foo nil} :bar)
;;=> nil
Вы можете получить всю запись карты (ключ и значение вместе) в виде двухэлементного вектора, используя find
:
(find {:foo :bar :baz :qux} 42)
;;=> nil
(find {:foo :bar :baz :qux} :foo)
;;=> [:foo :bar]
(find {:foo :bar :baz :qux} :bar)
;;=> nil
(find {:foo :bar :baz :qux} :baz)
;;=> [:baz :qux]
(find {:foo :bar :baz :qux} :qux)
;;=> nil
(find {:foo nil} :foo)
;;=> [:foo nil]
(find {:foo nil} :bar)
;;=> nil
Вы можете извлечь ключ или значение из записи карты с помощью key
или val
, соответственно:
(key (find {:foo :bar} :foo))
;;=> :foo
(val (find {:foo :bar} :foo))
;;=> :bar
Обратите внимание, что, хотя все записи карты Clojure являются векторами, не все векторы являются элементами карты. Если вы попытаетесь вызвать key
или val
на все, что не является записью карты, вы получите ClassCastException
:
(key [:foo :bar])
;; java.lang.ClassCastException:
(val [:foo :bar])
;; java.lang.ClassCastException:
Вы можете проверить, является ли что-то запись карты, используя запись map-entry?
сказуемое:
(map-entry? (find {:foo :bar} :foo))
;;=> true
(map-entry? [:foo :bar])
;;=> false
Вы можете использовать assoc
, чтобы получить карту , которая имеет все те же пары ключ-значение в существующей карте, с одной картографирования добавляемая или изменяемая:
(assoc {} :foo :bar)
;;=> {:foo :bar}
(assoc (assoc {} :foo :bar) :baz :qux)
;;=> {:foo :bar, :baz :qux}
(assoc {:baz :qux} :foo :bar)
;;=> {:baz :qux, :foo :bar}
(assoc {:foo :bar :baz :qux} :foo 42)
;;=> {:foo 42, :baz :qux}
(assoc {:foo :bar :baz :qux} :baz 42)
;;=> {:foo :bar, :baz 42}
Вы можете использовать dissoc
для получения карты, которая имеет все те же пары ключ-значение, что и существующая карта, возможно, с удалением одного отображения:
(dissoc {:foo :bar :baz :qux} 42)
;;=> {:foo :bar :baz :qux}
(dissoc {:foo :bar :baz :qux} :foo)
;;=> {:baz :qux}
(dissoc {:foo :bar :baz :qux} :bar)
;;=> {:foo :bar :baz :qux}
(dissoc {:foo :bar :baz :qux} :baz)
;;=> {:foo :bar}
(dissoc {:foo :bar :baz :qux} :qux)
;;=> {:foo :bar :baz :qux}
(dissoc {:foo nil} :foo)
;;=> {}
count
возвращает количество отображений в постоянное время:
(count {})
;;=> 0
(count (assoc {} :foo :bar))
;;=> 1
(count {:foo :bar :baz :qux})
;;=> 2
Вы можете получить последовательность всех записей на карте с помощью seq
:
(seq {})
;;=> nil
(seq {:foo :bar})
;;=> ([:foo :bar])
(seq {:foo :bar :baz :qux})
;;=> ([:foo :bar] [:baz :qux])
Опять же, карты неупорядочены, поэтому порядок элементов в последовательности, которую вы получаете, вызывая seq
на карте, не определен.
Вы можете получить последовательность только ключей или только значений на карте с помощью keys
или vals
, соответственно:
(keys {})
;;=> nil
(keys {:foo :bar})
;;=> (:foo)
(keys {:foo :bar :baz :qux})
;;=> (:foo :baz)
(vals {})
;;=> nil
(vals {:foo :bar})
;;=> (:bar)
(vals {:foo :bar :baz :qux})
;;=> (:bar :qux)
Clojure 1.9 добавляет буквальный синтаксис для более сжатого представления карты, где ключи используют одно и то же пространство имен. Обратите внимание, что карта в любом случае идентична (карта не «знает» пространство имен по умолчанию), это просто синтаксическое удобство.
;; typical map syntax
(def p {:person/first"Darth" :person/last "Vader" :person/email "[email protected]"})
;; namespace map literal syntax
(def p #:person{:first "Darth" :last "Vader" :email "[email protected]"})