clojure
Kolekcje i sekwencje
Szukaj…
Składnia
-
'()
→()
-
'(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"}
(wpisy są sortowane według klucza, gdy są używane jako sekwencja) -
#{}
→#{}
-
#{1 2 3 4 5}
→#{4 3 2 5 1}
(nieuporządkowany) -
(
hash-set
1 2 3 4 5)
→#{2 5 4 1 3}
(nieuporządkowany) -
(
sorted-set
2 5 4 3 1)
→#{1 2 3 4 5}
Kolekcje
Wszystkie wbudowane kolekcje Clojure są niezmienne i heterogeniczne, mają dosłowną składnię i obsługują funkcje conj
, count
i seq
.
-
conj
zwraca nową kolekcję, która jest równoważna istniejącej kolekcji z elementem „dodanym”, w „stałym” lub logarytmicznym czasie. Co dokładnie to oznacza, zależy od kolekcji. -
count
zwraca liczbę elementów w kolekcji w stałym czasie. -
seq
zwracanil
dla pustej kolekcji lub sekwencję elementów dla niepustej kolekcji w stałym czasie.
Listy
Lista jest oznaczona nawiasami:
()
;;=> ()
Lista Clojure to lista pojedynczo połączona . conj
„conjoins” nowy element do kolekcji w najbardziej efektywny lokalizacji. W przypadku list jest to na początku:
(conj () :foo)
;;=> (:foo)
(conj (conj () :bar) :foo)
;;=> (:foo :bar)
W przeciwieństwie do innych kolekcji niepuste listy są oceniane jako wywołania specjalnych formularzy, makr lub funkcji. Dlatego, chociaż (:foo)
jest dosłowną reprezentacją listy zawierającej :foo
jako jedyny element, ocena (:foo)
w REPL spowoduje zgłoszenie IllegalArgumentException
ponieważ słowa kluczowego nie można wywołać jako funkcji zerowej .
(:foo)
;; java.lang.IllegalArgumentException: Wrong number of args passed to keyword: :foo
Aby uniemożliwić Clojure ocenę niepustej listy, możesz ją quote
:
'(:foo)
;;=> (:foo)
'(:foo :bar)
;;=> (:foo :bar)
Niestety powoduje to, że elementy nie są oceniane:
(+ 1 1)
;;=> 2
'(1 (+ 1 1) 3)
;;=> (1 (+ 1 1) 3)
Z tego powodu zwykle będziesz chciał użyć list
, funkcji variadic, która ocenia wszystkie argumenty i wykorzystuje te wyniki do utworzenia listy:
(list)
;;=> ()
(list :foo)
;;=> (:foo)
(list :foo :bar)
;;=> (:foo :bar)
(list 1 (+ 1 1) 3)
;;=> (1 2 3)
count
zwraca liczbę elementów w stałym czasie:
(count ())
;;=> 0
(count (conj () :foo))
;;=> 1
(count '(:foo :bar))
;;=> 2
Możesz sprawdzić, czy coś jest listą za pomocą list?
orzec:
(list? ())
;;=> true
(list? '(:foo :bar))
;;=> true
(list? nil)
;;=> false
(list? 42)
;;=> false
(list? :foo)
;;=> false
Pierwszy element listy możesz uzyskać za pomocą peek
:
(peek ())
;;=> nil
(peek '(:foo))
;;=> :foo
(peek '(:foo :bar))
;;=> :foo
Możesz uzyskać nową listę bez pierwszego elementu za pomocą pop
:
(pop '(:foo))
;;=> ()
(pop '(:foo :bar))
;;=> (:bar)
Pamiętaj, że jeśli spróbujesz pop
pustą listę, otrzymasz IllegalStateException
:
(pop ())
;; java.lang.IllegalStateException: Can't pop empty list
Wreszcie, wszystkie listy są sekwencjami, więc możesz zrobić wszystko z listą, którą możesz zrobić z dowolną inną sekwencją. Rzeczywiście, z wyjątkiem pustej listy, wywołanie seq
na liście zwraca dokładnie ten sam obiekt:
(seq ())
;;=> nil
(seq '(:foo))
;;=> (:foo)
(seq '(:foo :bar))
;;=> (:foo :bar)
(let [x '(:foo :bar)]
(identical? x (seq x)))
;;=> true
Sekwencje
Sekwencja jest bardzo podobna do listy: jest niezmiennym obiektem, który może dać ci swój first
element lub rest
jego elementów w stałym czasie. Można również cons
truct nową sekwencję z istniejącej sekwencji i element trzymać na początku.
Możesz sprawdzić, czy coś jest sekwencją za pomocą seq?
orzec:
(seq? nil)
;;=> false
(seq? 42)
;;=> false
(seq? :foo)
;;=> false
Jak już wiesz, listy są sekwencjami:
(seq? ())
;;=> true
(seq? '(:foo :bar))
;;=> true
Wszystko, co otrzymujesz przez wywołanie seq
lub rseq
lub keys
lub vals
w niepustym zbiorze, jest również sekwencją:
(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
Pamiętaj, że wszystkie listy są sekwencjami, ale nie wszystkie sekwencje są listami. Podczas gdy listy obsługują peek
i pop
i count
w stałym czasie, generalnie sekwencja nie musi obsługiwać żadnej z tych funkcji. Jeśli spróbujesz wywołać peek
lub pop
w sekwencji, która nie obsługuje również interfejsu stosu Clojure, otrzymasz 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
Jeśli wywołasz count
w sekwencji, która nie implementuje count
w stałym czasie, nie pojawi się błąd; zamiast tego Clojure będzie przemierzał całą sekwencję aż do końca, a następnie zwróci liczbę elementów, które przemierzył. Oznacza to, że dla sekwencji ogólnych count
jest liniowa, a nie stała, w czasie. Możesz sprawdzić, czy coś obsługuje count
czasu stałego za pomocą counted?
orzec:
(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
Jak wspomniano powyżej, możesz użyć first
aby uzyskać pierwszy element sekwencji. Zauważ, że first
zadzwoni seq
ich argumentu, dzięki czemu może być używany na cokolwiek „seqable”, a nie tylko rzeczywistych sekwencji:
(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
Również jak wspomniano powyżej, możesz użyć rest
aby uzyskać sekwencję zawierającą wszystkie oprócz pierwszego elementu istniejącej sekwencji. Podobnie jak first
, wywołuje seq
w swoim argumencie. Jednak nie wywołuje seq
po swoim wyniku! Oznacza to, że jeśli wywołasz rest
na sekwencji, która zawiera mniej niż dwa elementy, otrzymasz return ()
zamiast nil
:
(rest nil)
;;=> ()
(rest '(:foo))
;;=> ()
(rest '(:foo :bar))
;;=> (:bar)
(rest [:foo])
;;=> ()
(rest [:foo :bar])
;;=> (:bar)
(rest {:foo :bar})
;;=> ()
(rest #{:foo})
;;=> ()
Jeśli chcesz odzyskać nil
gdy nie ma już więcej elementów w sekwencji, możesz użyć next
zamiast rest
:
(next nil)
;;=> nil
(next '(:foo))
;;=> nil
(next [:foo])
;;=> nil
Możesz użyć funkcji cons
aby utworzyć nową sekwencję, która zwróci pierwszy argument dla first
i drugi argument dla rest
:
(cons :foo nil)
;;=> (:foo)
(cons :foo (cons :bar nil))
;;=> (:foo :bar)
Clojure zapewnia dużą bibliotekę sekwencji z wieloma funkcjami do obsługi sekwencji. Ważną rzeczą w tej bibliotece jest to, że działa ona z wszystkim, co jest „seqable”, a nie tylko z listami. Dlatego koncepcja sekwencji jest tak przydatna; oznacza to, że pojedyncza funkcja, na przykład reduce
, działa doskonale w każdej kolekcji:
(reduce + '(1 2 3))
;;=> 6
(reduce + [1 2 3])
;;=> 6
(reduce + #{1 2 3})
;;=> 6
Innym powodem, dla którego sekwencje są przydatne, jest to, że ponieważ nie nakazują żadnej konkretnej implementacji first
i rest
, pozwalają na leniwe sekwencje, których elementy są realizowane tylko w razie potrzeby.
Biorąc pod uwagę wyrażenie, które utworzyłoby sekwencję, możesz owinąć to wyrażenie w makro lazy-seq
aby uzyskać obiekt, który działa jak sekwencja, ale faktycznie oceni to wyrażenie, gdy zostanie o to poproszony przez funkcję seq
, w który punkt zbuforuje wynik wyrażenia, a następnie przekaże first
i rest
wywołań do wyniku w pamięci podręcznej.
W przypadku sekwencji skończonych sekwencja leniwa zwykle działa tak samo jak równoważna sekwencja chętna:
(seq [:foo :bar])
;;=> (:foo :bar)
(lazy-seq [:foo :bar])
;;=> (:foo :bar)
Jednak różnica staje się widoczna dla nieskończonych sekwencji:
(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)
Wektory
Wektor jest oznaczony nawiasami kwadratowymi:
[]
;;=> []
[:foo]
;;=> [:foo]
[:foo :bar]
;;=> [:foo :bar]
[1 (+ 1 1) 3]
;;=> [1 2 3]
Oprócz użycia dosłownej składni możesz także użyć funkcji vector
do skonstruowania wektora:
(vector)
;;=> []
(vector :foo)
;;=> [:foo]
(vector :foo :bar)
;;=> [:foo :bar]
(vector 1 (+ 1 1) 3)
;;=> [1 2 3]
Możesz sprawdzić, czy coś jest wektorem za pomocą tego vector?
orzec:
(vector? [])
;;=> true
(vector? [:foo :bar])
;;=> true
(vector? nil)
;;=> false
(vector? 42)
;;=> false
(vector? :foo)
;;=> false
conj
dodaje elementy na końcu wektora:
(conj [] :foo)
;;=> [:foo]
(conj (conj [] :foo) :bar)
;;=> [:foo :bar]
(conj [] :foo :bar)
;;=> [:foo :bar]
count
zwraca liczbę elementów w stałym czasie:
(count [])
;;=> 0
(count (conj [] :foo))
;;=> 1
(count [:foo :bar])
;;=> 2
Ostatni element wektora można uzyskać za pomocą peek
:
(peek [])
;;=> nil
(peek [:foo])
;;=> :foo
(peek [:foo :bar])
;;=> :bar
Możesz uzyskać nowy wektor bez ostatniego elementu, używając pop
:
(pop [:foo])
;;=> []
(pop [:foo :bar])
;;=> [:foo]
Pamiętaj, że jeśli spróbujesz rozbić pusty wektor, otrzymasz IllegalStateException
:
(pop [])
;; java.lang.IllegalStateException: Can't pop empty vector
W przeciwieństwie do list, wektory są indeksowane. Możesz uzyskać element wektora przez indeks w „stałym” czasie, używając get
:
(get [:foo :bar] 0)
;;=> :foo
(get [:foo :bar] 1)
;;=> :bar
(get [:foo :bar] -1)
;;=> nil
(get [:foo :bar] 2)
;;=> nil
Ponadto same wektory są funkcjami, które pobierają indeks i zwracają element pod tym indeksem:
([:foo :bar] 0)
;;=> :foo
([:foo :bar] 1)
;;=> :bar
Jednakże, jeśli można nazwać wektor z nieprawidłowym wskaźnikiem, dostaniesz IndexOutOfBoundsException
zamiast nil
:
([:foo :bar] -1)
;; java.lang.IndexOutOfBoundsException:
([:foo :bar] 2)
;; java.lang.IndexOutOfBoundsException:
Możesz uzyskać nowy wektor o innej wartości przy określonym indeksie, używając assoc
:
(assoc [:foo :bar] 0 42)
;;=> [42 :bar]
(assoc [:foo :bar] 1 42)
;;=> [:foo 42]
Jeśli zdasz indeks równy count
wektorów, Clojure doda element tak, jakbyś użył conj
. Jeśli jednak przekażesz indeks, który jest ujemny lub większy niż count
, otrzymasz 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:
Możesz uzyskać sekwencję elementów w wektorze za pomocą seq
:
(seq [])
;;=> nil
(seq [:foo])
;;=> (:foo)
(seq [:foo :bar])
;;=> (:foo :bar)
Ponieważ wektory są indeksowane, można również uzyskać odwróconą sekwencję elementów wektora za pomocą rseq
:
(rseq [])
;;=> nil
(rseq [:foo])
;;=> (:foo)
(rseq [:foo :bar])
;;=> (:bar :foo)
Zauważ, że chociaż wszystkie listy są sekwencjami, a sekwencje są wyświetlane w taki sam sposób jak listy, nie wszystkie sekwencje są listami!
'(:foo :bar)
;;=> (:foo :bar)
(seq [:foo :bar])
;;=> (:foo :bar)
(list? '(:foo :bar))
;;=> true
(list? (seq [:foo :bar]))
;;=> false
(list? (rseq [:foo :bar]))
;;=> false
Zestawy
Podobnie jak mapy, zestawy są asocjacyjne i nieuporządkowane. W przeciwieństwie do map, które zawierają mapowania od kluczy do wartości, zestawy zasadniczo mapują od kluczy do samych siebie.
Zestaw jest oznaczony nawiasami klamrowymi poprzedzonymi oktotorpą:
#{}
;;=> #{}
#{:foo}
;;=> #{:foo}
#{:foo :bar}
;;=> #{:bar :foo}
Podobnie jak w przypadku map, kolejność pojawiania się elementów w dosłownym zbiorze nie ma znaczenia:
(= #{:foo :bar} #{:bar :foo})
;;=> true
Możesz sprawdzić, czy coś jest zestawem, używając set?
orzec:
(set? #{})
;;=> true
(set? #{:foo})
;;=> true
(set? #{:foo :bar})
;;=> true
(set? nil)
;;=> false
(set? 42)
;;=> false
(set? :foo)
;;=> false
Możesz przetestować, czy mapa zawiera dany przedmiot w „stałym” czasie, używając contains?
orzec:
(contains? #{} :foo)
;;=> false
(contains? #{:foo} :foo)
;;=> true
(contains? #{:foo} :bar)
;;=> false
(contains? #{} nil)
;;=> false
(contains? #{nil} nil)
;;=> true
Ponadto same zestawy są funkcjami, które pobierają element i zwracają ten element, jeśli jest obecny w zestawie, lub nil
jeśli nie jest:
(#{} :foo)
;;=> nil
(#{:foo} :foo)
;;=> :foo
(#{:foo} :bar)
;;=> nil
(#{} nil)
;;=> nil
(#{nil} nil)
;;=> nil
Możesz użyć conj
aby uzyskać zestaw, który zawiera wszystkie elementy istniejącego zestawu oraz jeden dodatkowy element:
(conj #{} :foo)
;;=> #{:foo}
(conj (conj #{} :foo) :bar)
;;=> #{:bar :foo}
(conj #{:foo} :foo)
;;=> #{:foo}
Możesz użyć disj
aby uzyskać zestaw, który zawiera wszystkie elementy istniejącego zestawu, minus jeden element:
(disj #{} :foo)
;;=> #{}
(disj #{:foo} :foo)
;;=> #{}
(disj #{:foo} :bar)
;;=> #{:foo}
(disj #{:foo :bar} :foo)
;;=> #{:bar}
(disj #{:foo :bar} :bar)
;;=> #{:foo}
count
zwraca liczbę elementów w stałym czasie:
(count #{})
;;=> 0
(count (conj #{} :foo))
;;=> 1
(count #{:foo :bar})
;;=> 2
Możesz uzyskać sekwencję wszystkich elementów w zestawie za pomocą seq
:
(seq #{})
;;=> nil
(seq #{:foo})
;;=> (:foo)
(seq #{:foo :bar})
;;=> (:bar :foo)
Mapy
W przeciwieństwie do listy, która jest sekwencyjną strukturą danych, oraz wektora, który jest zarówno sekwencyjny, jak i asocjacyjny, mapa jest wyłącznie asocjacyjną strukturą danych. Mapa składa się z zestawu odwzorowań od kluczy do wartości. Wszystkie klucze są unikalne, dlatego mapy obsługują „ciągłe” wyszukiwanie od kluczy do wartości.
Mapa jest oznaczona nawiasami klamrowymi:
{}
;;=> {}
{:foo :bar}
;;=> {:foo :bar}
{:foo :bar :baz :qux}
;;=> {:foo :bar, :baz :qux}
Każda para dwóch elementów jest parą klucz-wartość. Na przykład pierwsza mapa powyżej nie ma mapowań. Drugi ma jedno mapowanie, od klucza :foo
do wartości :bar
. Trzeci ma dwa odwzorowania, jeden od klucza :foo
do wartości :bar
, a drugi od klucza :baz
do wartości :qux
. Mapy są z natury nieuporządkowane, więc kolejność ich wyświetlania nie ma znaczenia:
(= {:foo :bar :baz :qux}
{:baz :qux :foo :bar})
;;=> true
Możesz sprawdzić, czy coś jest mapą za pomocą map?
orzec:
(map? {})
;;=> true
(map? {:foo :bar})
;;=> true
(map? {:foo :bar :baz :qux})
;;=> true
(map? nil)
;;=> false
(map? 42)
;;=> false
(map? :foo)
;;=> false
Możesz sprawdzić, czy mapa zawiera dany klucz w „stałym” czasie za pomocą contains?
orzec:
(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
Możesz uzyskać wartość powiązaną z kluczem za pomocą 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
Ponadto same mapy są funkcjami, które pobierają klucz i zwracają wartość związaną z tym kluczem:
({: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
Możesz uzyskać cały wpis mapy (klucz i wartość razem) jako dwuelementowy wektor, używając 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
Możesz wyodrębnić klucz lub wartość z wpisu mapy, używając odpowiednio key
lub val
:
(key (find {:foo :bar} :foo))
;;=> :foo
(val (find {:foo :bar} :foo))
;;=> :bar
Zauważ, że chociaż wszystkie wpisy map Clojure są wektorami, nie wszystkie wektory są wpisami map. Jeśli spróbujesz wywołać key
lub val
na czymkolwiek, co nie jest wpisem na mapie, otrzymasz ClassCastException
:
(key [:foo :bar])
;; java.lang.ClassCastException:
(val [:foo :bar])
;; java.lang.ClassCastException:
Możesz sprawdzić, czy coś jest wpisem na map-entry?
używając wpisu map-entry?
orzec:
(map-entry? (find {:foo :bar} :foo))
;;=> true
(map-entry? [:foo :bar])
;;=> false
Za pomocą narzędzia assoc
można uzyskać mapę zawierającą wszystkie te same pary klucz-wartość, co istniejąca mapa, z dodanym lub zmienionym jednym mapowaniem:
(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}
Możesz użyć dissoc
aby uzyskać mapę, która ma wszystkie te same pary klucz-wartość jak istniejąca mapa, z możliwym usunięciem jednego mapowania:
(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
zwraca liczbę mapowań w stałym czasie:
(count {})
;;=> 0
(count (assoc {} :foo :bar))
;;=> 1
(count {:foo :bar :baz :qux})
;;=> 2
Możesz uzyskać sekwencję wszystkich wpisów na mapie za pomocą seq
:
(seq {})
;;=> nil
(seq {:foo :bar})
;;=> ([:foo :bar])
(seq {:foo :bar :baz :qux})
;;=> ([:foo :bar] [:baz :qux])
Ponownie, mapy są nieuporządkowane, więc kolejność elementów w sekwencji, które można uzyskać dzwoniąc seq
na mapie jest niezdefiniowane.
Możesz uzyskać sekwencję samych kluczy lub tylko wartości na mapie, używając odpowiednio keys
lub 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 dodaje dosłowną składnię, aby bardziej zwięźle przedstawić mapę, w której klucze mają tę samą przestrzeń nazw. Zauważ, że mapa w obu przypadkach jest identyczna (mapa nie „zna” domyślnej przestrzeni nazw), jest to jedynie wygoda składniowa.
;; 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]"})