Поиск…


Синтаксис

  • '()()
  • '(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]"})


Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow