Статьи

Clojure: получить, получить, содержит? И некоторые

Clojure предоставляет функцию get, которая возвращает значение, сопоставленное ключу в наборе или карте. В документации показан пример: (получить ключ карты). Хотя это совершенно верно, я склонен использовать наборы и карты в качестве функций, когда получить это так просто.

Например, я бы использовал ({«FSU» 31 «UF» 7} «FSU»), если бы я хотел значение ключа «FSU». Гораздо менее вероятно, что я буду использовать (get {«FSU» 31 «UF» 7} «FSU»), в основном потому, что в первом примере меньше набирается.

Однако, если я делаю что-то более сложное, я считаю полезной функцию get. Часто мне нравится использовать комбинацию get и -> или — >>.

В следующем примере некоторые json-данные преобразуются в карту замыкания,и вытягивает значение из клавиши «FSU».

(-> json-data read-json (get "FSU"))

Стоит также отметить, что в нашем примере мы должны использовать get, поскольку строки не являются функциями Clojure. Если бы вместо этого мы решили сделать наши ключевые слова ключевыми словами, мы могли бы выбрать одно из следующих решений. Я не верю, что есть правильное или неправильное решение; который вы используете, скорее всего, будет личным предпочтением

(-> json-data (read-json true) (get :FSU))
(-> json-data (read-json true) :FSU)

Мы можем изменить пример и предположить, что вложенный json приводит к следующей карте замыкания: {«timestamp» 1291578985220 «scores» {«FSU» 31 «UF» 7}} На

основе предыдущего примера мы могли бы использовать слегка измененную версию чтобы получить оценку для бывшего СССР.

(-> json-data read-json (get "scores") (get "FSU"))

Однако получение вложенных значений является достаточно распространенным явлением, поэтому Clojure предоставляет функцию, специально разработанную для удовлетворения этой потребности: get-in

Функция get-in возвращает значение во вложенной ассоциативной структуре, когда ей дана последовательность ключей. Используя get-in, вы можете заменить последний пример следующим кодом.

(-> json-data read-json (get-in ["scores" "FSU"]))

Функция get-in очень полезна при работе с вложенными структурами; однако, есть одна ошибка, с которой я столкнулся. Ниже показан сеанс REPL и то, что возвращается с различными ключами.

user=> (get-in {"timestamp" 1291578985220 "scores" {"FSU" 31 "UF" 7}} ["scores" "FSU"])
31

user=> (get-in {"timestamp" 1291578985220 "scores" {"FSU" 31 "UF" 7}} ["scores"])      
{"FSU" 31, "UF" 7}

user=> (get-in {"timestamp" 1291578985220 "scores" {"FSU" 31 "UF" 7}} [])        
{"timestamp" 1291578985220, "scores" {"FSU" 31, "UF" 7}}

user=> (get-in {"timestamp" 1291578985220 "scores" {"FSU" 31 "UF" 7}} nil)
{"timestamp" 1291578985220, "scores" {"FSU" 31, "UF" 7}}

Все выглядит достаточно логично; однако, если вы извлекаете последовательность клавиш из другого места, вы можете получить неожиданные результаты. В следующем примере показано, как простая ошибка может привести к ошибке.

user=> (def score-key-seqs {"FSU" ["scores" "FSU"]})                             
#'user/score-key-seqs

user=> (get-in {"timestamp" 1291578985220 "scores" {"FSU" 31 "UF" 7}} (score-key-seqs "FSU"))
31

user=> (get-in {"timestamp" 1291578985220 "scores" {"FSU" 31 "UF" 7}} (score-key-seqs "UF")) 
{"timestamp" 1291578985220, "scores" {"FSU" 31, "UF" 7}}

Если вы всегда ожидаете число и вместо этого получаете карту, все может не сработать.

Также стоит отметить, что как get, так и get-in позволяют указывать значения по умолчанию. Вы можете проверить документацию на clojure.org для получения дополнительной информации о значениях по умолчанию.

Вам не всегда нужно получать значение, иногда достаточно знать, что ключ находится на карте или в наборе. В общем, я использую значение, возвращаемое картой или набором, чтобы определить, существует ли ключ — этот фрагмент используется в следующем фрагменте.

(if (a-map :key) 
  (do-true-behaviors) 
  (do-false-behaviors))

Однако этот шаблон завершается ошибкой, если значение: key равно nil. Если возможно, что значение может быть ноль, вы можете использовать Clojure’s
содержит? функция. Содержится? функция возвращает истину, если ключ присутствует в данной коллекции, в противном случае возвращает ложь. Следующий код, вставленный из сеанса REPL, демонстрирует, что содержит? отлично работает с нуля.

user=> (contains? {:foo nil} :foo)
true

Содержится? функция хорошо работает с наборами и картами; однако, если вы попытаетесь использовать его в списке, вы можете получить удивительные результаты.

user=> (contains? [1 3 4] 2)
true

Для численно проиндексированных коллекций, таких как векторы и массивы Java, содержится? Функция проверяет, находится ли числовая клавиша в диапазоне индексов. Документация Clojure рекомендует использовать
некоторые функции, если вы ищете элемент в списке.

Функция some возвращает первое логическое истинное значение предиката для любого элемента в списке, иначе nil. В следующем сеансе REPL показано, как можно использовать набор в качестве предиката с некоторыми, чтобы определить, найдено ли значение в списке.

user=> (some #{2} [1 3 4])  
nil
user=> (some #{1} [1 3 4])
1

Clojure предоставляет различные функции для работы с картами и наборами. На первый взгляд, некоторые из них могут показаться излишними; однако, если вы будете тратить больше времени на работу с наборами и картами, вы начнете понимать тонкие различия и ценность, которую они предоставляют.

С http://blog.jayfields.com/2010/12/clojure-get-get-in-contains-and-some.html