C'est un scénario que j'ai rencontré plusieurs fois, mais je n'ai pas trouvé d'approche idiomatique pour cela ...

Supposons que l'on souhaite utiliser une fonction self-pred auto-définie pour filtrer un seq. Cette fonction self-pred renvoie nil pour les éléments indésirables et des informations utiles pour les éléments recherchés. Il est souhaitable de conserver les valeurs self-pred évaluées pour ces éléments recherchés.

Ma solution générale est:

;; self-pred is a pred function which returns valuable info
;; in general, they are unique and can be used as key
(let [new-seq (filter self-pred aseq)]
   (zipmap (map self-pred new-seq) new-seq))

Fondamentalement, il s'agit d'appeler self-pred deux fois sur tous les éléments voulus. Je sens que c'est si moche ...

Je me demande s'il existe de meilleures façons. Très apprécié pour toute contribution!

2
Gang Liang 16 nov. 2017 à 12:15

4 réponses

Meilleure réponse

J'utilise ce genre d'extrait:

(keep #(some->> % self-pred (vector %)) data)

Comme ça:

user> (keep #(some->> % rseq (vector %)) [[1 2] [] [3 4]])
;;=> ([[1 2] (2 1)] [[3 4] (4 3)])

Ou si vous aimez un résultat plus détaillé:

user> (keep #(some->> % rseq (hash-map :data % :result)) [[1 2] [] [3 4]])
;;=> ({:result (2 1), :data [1 2]} {:result (4 3), :data [3 4]})
4
leetwinski 16 nov. 2017 à 10:16

Dans ces scénarios, vous pouvez utiliser keep, mais vous devez modifier votre fonction "prédicat" pour renvoyer toutes les informations dont vous avez besoin, ou nil, pour chaque élément.

Par exemple:

(keep (fn [item] 
         (when-let [tested (some-test item)] 
           (assoc item :test-output tested))) aseq)
5
Joost Diepenmaat 16 nov. 2017 à 09:30

Si self-pred ne garantit aucune création de clé en double pour différentes valeurs, alors j'atteindrais pour réduire (car assoc la même clé deux fois remplacera la paire de valeur de clé d'origine):

(reduce #(if-let [k (self-pred %2)]
          (assoc %1 k %2)
          %1)
 {}
 aseq)

Sinon, nous pouvons utiliser group-by pour obtenir un résultat similaire:

(dissoc (group-by self-pred aseq) nil)

Bien que ce ne soit pas le même puisque les valeurs seront dans des vecteurs: {k1 [v1 ..], k2 [..], ..}. mais cela garantit que toutes les valeurs sont conservées.

-1
asedge 16 nov. 2017 à 21:25

Je ne me soucierais pas de keep, mais utiliserais simplement une carte et un filtre simples comme ceci:

(def data (range 6))

(def my-pred odd?)

(defn zip [& colls] (apply map vector colls))  ; like Python zip
(defn filter-with-pred
  [vals pred]
  (filter #(first %)
    (zip (map pred vals) vals)))

(println (filter-with-pred data my-pred))

Avec résultat:

([true 1] [true 3] [true 5])
1
Alan Thompson 16 nov. 2017 à 15:13
47325664