2012-09-13 17 views
14

Tenía la impresión de que los seces perezosos siempre se fragmentaban.En Clojure, ¿los seces perezosos siempre se fragmentan?

=> (take 1 (map #(do (print \.) %) (range))) 
(................................0) 

Como era de esperar 32 puntos se imprimen porque el SEC perezoso devuelto por range está fragmentada en 32 trozos de elementos. Sin embargo, cuando en lugar de range Trato esto con mi propia función get-rss-feeds, la SEC perezoso ya no está fragmentada:

=> (take 1 (map #(do (print \.) %) (get-rss-feeds r))) 
(."http://wholehealthsource.blogspot.com/feeds/posts/default") 

Sólo un punto se imprime, así que supongo que los perezosos-ss devuelto por get-rss-feeds no está fragmentada. En efecto:

=> (chunked-seq? (seq (range))) 
true 

=> (chunked-seq? (seq (get-rss-feeds r))) 
false 

Aquí está la fuente de get-rss-feeds:

(defn get-rss-feeds 
    "returns a lazy seq of urls of all feeds; takes an html-resource from the enlive library" 
    [hr] 
    (map #(:href (:attrs %)) 
     (filter #(rss-feed? (:type (:attrs %))) (html/select hr [:link]))) 

lo que parece que chunkiness depende de cómo se produce la SEC perezoso. Eché un vistazo al origen de la función range y hay indicios de que se está implementando de una manera "gruesa". Así que estoy un poco confundido sobre cómo funciona esto. ¿Alguien puede aclarar?


He aquí por qué necesito saberlo.

tengo que siguiente código: (get-rss-entry (get-rss-feeds h-res) url)

La llamada a get-rss-feeds devuelve una secuencia perezosa de URLs de los feeds que tengo que examinar.

La llamada a get-rss-entry busca una entrada en particular (cuyo: campo de enlace coincide con el segundo argumento de get-rss-entry). Examina la secuencia diferida devuelta por get-rss-feeds. La evaluación de cada elemento requiere una solicitud http en la red para obtener un nuevo feed rss. Para minimizar el número de solicitudes http, es importante examinar la secuencia una a una y detenerla tan pronto como haya una coincidencia.

Aquí está el código:

(defn get-rss-entry 
    [feeds url] 
    (ffirst (drop-while empty? (map #(entry-with-url % url) feeds)))) 

entry-with-url devuelve una secuencia lenta de partidos o una secuencia vacía si no hay ninguna coincidencia.

Probé esto y parece funcionar correctamente (evaluando una URL de feed a la vez). Pero estoy preocupado de que en algún lugar, de alguna manera comience a comportarse de una manera "fornida" y comenzará a evaluar 32 avances a la vez. Sé que hay una manera de avoid chunky behavior as discussed here, pero parece que ni siquiera es necesario en este caso.

¿Estoy usando lazy seq non-idiomatically? ¿El bucle/repetición sería una mejor opción?

+0

Parece ser que es una secuencia única "fragmentada" si utiliza las diversas funciones del pedazo en 'clojure.core' y/o su secuencia implementa el' IChunk' y 'IChunkedSeq' interfaces. Actualmente (en 1.4.0), estos no están documentados. – noahlz

+0

¿Qué versión de clojure estás usando? –

+0

Estoy usando Clojure v1.4 –

Respuesta

3

Dependiendo de la vaguedad de Chunking parece imprudente como mencionas arriba. Explicitamente, "desunir" en los casos donde realmente se necesita que no se fragmente, también es sabio porque si en algún otro momento su código cambia de una manera que lo rompe, las cosas no se romperán. En otra nota, si necesita que las acciones sean secuenciales, los agentes son una gran herramienta puede enviar las funciones de descarga a un agente y luego se ejecutarán una a la vez independientemente de cómo evalúe la función. En algún momento es posible que desee pmap su secuencia y, a continuación, incluso un-chunking no funcionará aunque el uso de un átomo seguirá funcionando correctamente.

+2

¿Podría ampliar esto con un boceto de código de muestra? ¿Te refieres a agentes en lugar de átomos? – noahlz

+0

¿te refieres a agente en lugar de átomo aquí? porque las funciones se proporcionan para intercambiar! se reintentará – noisesmith

+0

s/atom/agent/g lo siento. Mis dedos traicionan mi cerebro y presiono las teclas equivocadas ... arregladas. –

5

Lazy seqs are no siempre fragmentada - depende de cómo se producen.

Por ejemplo, la SEC perezoso producido por esta función no está fragmentada:

(defn integers-from [n] 
    (lazy-seq (cons n (do (print \.) (integers-from (inc n)))))) 

(take 3 (integers-from 3)) 
=> (..3 .4 5) 

Pero muchas funciones incorporadas en otra clojure sí producen SEQs fragmentados por razones de rendimiento (por ejemplo range)

+1

Es muy importante agregar que 'map' y' filter' ambos pueden producir seqs fragmentados. Mezclar los efectos secundarios y la pereza es una receta para errores sutiles. Los transductores ayudan aquí. –

11

Usted está derecho a estar preocupado Su get-rss-entry de hecho llamará al entry-with-url más de lo estrictamente necesario si el parámetro feeds es una colección que devuelve seqs fragmentados. Por ejemplo, si feeds es un vector, map operará en trozos completos a la vez.

Este problema se aborda directamente en Fogus' alegría de Clojure, con la función seq1 define en el capítulo 12:

(defn seq1 [s] 
    (lazy-seq 
    (when-let [[x] (seq s)] 
     (cons x (seq1 (rest s)))))) 

usted podría utilizar este derecho donde se sabe que quiere sea posible el más pereza, a la derecha antes de llamar a entry-with-url:

 
(defn get-rss-entry 
    [feeds url] 
    (ffirst (drop-while empty? (map #(entry-with-url % url) (seq1 feeds))))) 
+0

Muchas gracias. BTW Acabo de terminar el libro y mi juego Clojure pasó al siguiente nivel. No puedo esperar a la versión actualizada. –

+0

Vale la pena señalar que esta llamada de desconexión a 'seq1' debe hacerse _at fuente_. Si está recibiendo una secuencia diferida de 'map' sobre una secuencia fragmentada, por ejemplo, no tiene suerte,' map' va a mirar hacia adelante sin importar lo que haga. – Thom

Cuestiones relacionadas