Статьи

Clojure: ключи выбора, значения выбора и значения применения

Clojure предоставляет функции get и get-in для возврата значений из карты и функцию select-keys для возврата новой карты только с указанными ключами. Clojure не предоставляет функцию, которая возвращает список значений; однако очень легко создать такую ​​функцию (которую я называю select-values). Если у вас есть возможность выбора значений, становится очень легко создать функцию, которая применяет функцию к выбранным значениям (которую я называю apply-values).

Функция select-keys возвращает карту, содержащую только записи указанных ключей. В следующем (вставленном) сеансе REPL показано несколько вариантов поведения клавиш выбора.

user=> (select-keys {:a 1 :b 2} [:a])   
{:a 1}
user=> (select-keys {:a 1 :b 2} [:a :b])
{:b 2, :a 1}
user=> (select-keys {:a 1 :b 2} [:a :b :c])
{:b 2, :a 1}
user=> (select-keys {:a 1 :b 2} [])  
{}
user=> (select-keys {:a 1 :b 2} nil)
{}

Функция клавиш выбора полезна во многих случаях; однако иногда вас интересует только выбор значений определенных ключей на карте. Простое решение состоит в том, чтобы вызвать select-keys и затем vals. Ниже вы можете найти результаты применения этой идеи.

user=> (def select-values (comp vals select-keys))
#'user/select-values
user=> (select-values {:a 1 :b 2} [:a])           
(1)
user=> (select-values {:a 1 :b 2} [:a :b])        
(2 1)
user=> (select-values {:a 1 :b 2} [:a :b :c])     
(2 1)
user=> (select-values {:a 1 :b 2} [])             
nil
user=> (select-values {:a 1 :b 2} nil)
nil

Реализация select-values ​​из приведенного выше может быть достаточной для того, что вы делаете, но есть две вещи, на которые стоит обратить внимание: в случаях, когда вы можете ожидать пустой список, вы видите nil; и значения находятся не в том порядке, в котором ключи были указаны. Учитывая, что (стандартные) карты не отсортированы, вы не можете быть уверены в порядке значений.

(бок . Примечание: Если вы обеспокоены микросекундами, это также было сообщено , что выберите клавиши немного медленно / мусор тяжелый)

Альтернативное определение отдельных значений использует
уменьшить функцию и тянет значения по ключу и постепенно строит (вектор) результат.

user=> (defn select-values [map ks]
         (reduce #(conj %1 (map %2)) [] ks))
#'user/select-values
user=> (select-values {:a 1 :b 2} [:a])      
[1]
user=> (select-values {:a 1 :b 2} [:a :b])   
[1 2]
user=> (select-values {:a 1 :b 2} [:a :b :c])
[1 2 nil]
user=> (select-values {:a 1 :b 2} [])        
[]
user=> (select-values {:a 1 :b 2} nil)       
[]

Новая функция select-values ​​возвращает значения по порядку и возвращает пустой вектор в случаях, когда предыдущие примеры возвращали ноль, но у нас есть новая проблема: указанные ключи, которых нет на карте, теперь включаются в вектор как ноль , Эта проблема легко решается путем добавления вызова
функции
удаления .

Реализация, которая включает удаление nils, может быть найдена ниже.

user=> (defn select-values [map ks]
         (remove nil? (reduce #(conj %1 (map %2)) [] ks)))
#'user/select-values
user=> (select-values {:a 1 :b 2} [:a])                          
(1)
user=> (select-values {:a 1 :b 2} [:a :b])                       
(1 2)
user=> (select-values {:a 1 :b 2} [:a :b :c])                    
(1 2)
user=> (select-values {:a 1 :b 2} [])                            
()
user=> (select-values {:a 1 :b 2} nil)                           
()

Не существует «правильной» реализации для select-values. Если вы не заботитесь о порядке и nil — разумное возвращаемое значение: первая реализация — правильный выбор из-за ее краткого определения. Если вы заботитесь о порядке и производительности: вторая реализация может быть правильным выбором. Если вы хотите что-то, что следует принципу наименьшего удивления: третья реализация, вероятно, является правильным выбором Вы должны решить, что лучше для вашего контекста. На самом деле, вот еще несколько реализаций, которые могут быть лучше в зависимости от вашего контекста.

user=> (defn select-values [m ks] 
         (map #({:a 1 :b 2} %) ks))             
#'user/select-values
user=> (select-values {:a 1 :b 2} [:a])                                                                                     
(1)
user=> (select-values {:a 1 :b 2} [:a :b :c])                                                                               
(1 2 nil)
user=> (defn select-values [m ks] 
         (reduce #(if-let [v ({:a 1 :b 2} %2)] (conj %1 v) %1) [] ks))
#'user/select-values
user=> (select-values {:a 1 :b 2} [:a])                                                        
[1]
user=> (select-values {:a 1 :b 2} [:a :b :c])                                                  
[1 2]

Извлечение значений из карты полезно, но обычно это не конечная цель. Если вы обнаружите, что извлекаете значения из карты, вполне вероятно, что вы захотите применить функцию к извлеченным значениям. Имея это в виду, я обычно определяю функцию apply-values, которая возвращает результат применения функции к значениям, возвращаемым указанными ключами.

Хорошим примером этого является возврат итогов для позиции, представленной в виде карты. Учитывая карту, в которой указана позиция стоимостью 5 долларов США и имеющая количество 4, вы можете использовать (* количество цены), чтобы определить общую цену позиции.

Используя нашу ранее определенную функцию select-values, мы можем сделать работу самостоятельно, как показано в примере ниже.

user=> (let [[price quantity] (select-values {:price 5 :quantity 4 :upc 1123} [:price :quantity])]                          
         (* price quantity))
20

Пример выше работает отлично; однако применение функции к значениям карты кажется довольно общей операцией, которую можно легко извлечь в ее собственную функцию (функция apply-values). Пример ниже показывает определение и использование моего определения apply-values.

user=> (defn apply-values [map f & ks]          
         (apply f (select-values map ks)))
#'user/apply-values
user=> (apply-values {:price 5 :quantity 4 :upc 1123} * :price :quantity)
20

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

 

С http://blog.jayfields.com/2011/01/clojure-select-keys-select-values-and.html