2012-07-24 14 views
7

I tienen una secuencia de mapa de pares como este (actualmente cerca de 17.000 pares)iterar sobre mapa clojure pares (circular)

(def myseq '({:name "Peter" :rank 2222} {:name "Anna" :rank 111})) 

I desea filtrar pares específicos en una nueva secuencia con

(filter (fn [x] (> x 222)) (:rank (first myseq))) 

He estado tratando de iterar con el bucle de esta manera, pero sigo teniendo muerte de hilo. Además, si utilizo el filtro en una sola colección de mapas, simplemente devuelve una nueva secuencia, ¿no estoy seguro de si necesito crear una aquí?

(defn remove-lower [number myseq] 
    (loop [i 0] 
     (if (= i (count file)) 
      (println "done") 
      (filter [x] (> x number)) 
       (:rank (first myseq)))) 
    (recur (rest myseq)))) 

Conviene por último garantizar es un bucle de la forma más eficiente para obtener la nueva secuencia de pares?

mejor, J

Respuesta

8

No hay necesidad de bucle/repiten aquí. filtrar ya se recorre en iteración una ss para usted:

(filter (fn [entry] (> (:rank entry) 220)) myseq) 
+0

¡Aún mejor! Así es como intenté hacerlo primero, pero no tuve éxito. Gracias. –

+5

'(filtro # (> (: rango%) 220) myseq)' – Ankur

+0

Muchas gracias chicos, el código ahora está funcionando y muy rápido también! –

6

Lo primero que debe saber es que (la mayoría) de las estructuras de datos en clojure son inmutables y la mayoría de las funciones son, así, funcional. Eso significa que no tienen efectos secundarios. En su caso filter no cambia la secuencia de ninguna manera, devuelve una nueva, que contiene solo los elementos no filtrados.

Así, para filtrar myseq que hay que hacer algo como:

(def filtered-seq (filter (fn [x] ...) myseq)) 

Filtro llamará a la función en repetidas ocasiones, la unión x para el elemento actualmente filtrada en myseq. Es decir, la primera vez estará obligado a {:name "Peter" :rank 2222}, luego a {:name "Anna" :rank 111}. El filtered-seq contendrá solo los elementos, para los cuales la función devolvió verdadero. myseqno se modificará!

lo tanto, usted quiere dejar sólo los elementos con :rank superior a 222:

(filter (fn [x] (> (:rank x) 222)) myseq) 

eso es todo. Y una cosa más sobre el filtro es que es flojo. Es decir, los elementos de la colección devuelta se "realizan" (o computan) solo cuando se necesitan.

No necesita usar loop para esto, ya que filter hace el trabajo bien, y loop no es flojo.

Dicho esto, su loop no funciona porque tiene varios problemas:

  1. recur está fuera del loop. En este caso, clojure volverá al principio de la función.
  2. que necesita para construir un valor de retorno y lo que necesita para mantener el elemento "actual"
  3. es necesario comprobar adecuadamente para la condición final

El código podría ser algo como esto (no probado):

(defn remove-lower [number myseq] 
    (loop [sq myseq res []] 
    (if (empty? sq) 
     res 
     (let [current (first sq)] 
      (if (> (:rank current) number) 
       (recur (rest sq) (conj res current)) 
       (recur (rest sq) res)))))) 

Nota cómo:

  1. recur es ahora dentro de la loop
  2. res contiene el valor de retorno y sq contiene la secuencia Actualmente izquierda
  3. cada recur pasa los nuevos valores de sq y res para la siguiente iteración
  4. sq es "shrinked" con cada iteración, por lo que el bucle con el tiempo se salir a menos que myseq sea infinito. Contraste esto a filter, que maneja secuencias infinitas muy bien.

Como ve, esto es más difícil de leer y menos general que filter y también está ansioso (no flojo).

+0

Gracias Ivant, ¡fantástico comentario! –