2012-09-13 16 views
14

Avevo l'impressione che i pigri seq fossero sempre frammentati.In Clojure, i seq pigri sono sempre misti?

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

come previsto 32 punti sono stampati perché la ss pigri restituito da range è chunked in 32 blocchi di elementi. Tuttavia, quando invece di range provo questo con la mia propria funzione get-rss-feeds, la ss pigro non è più Chunked:

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

un solo punto viene stampato, quindi credo che il pigro-seq restituito da get-rss-feeds non è chunked. Infatti:

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

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

Ecco la fonte per 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]))) 

Così sembra che chunkiness dipende da come viene prodotta la ss pigro. Ho dato una sbirciata alla fonte per la funzione range e ci sono suggerimenti che sono stati implementati in modo "massiccio". Quindi sono un po 'confuso su come funziona. Qualcuno può chiarire?


Ecco perché ho bisogno di sapere.

devo seguente codice: (get-rss-entry (get-rss-feeds h-res) url)

La chiamata a get-rss-feeds restituisce una sequenza pigro di URL di feed che ho bisogno di esaminare.

La chiamata a get-rss-entry cerca una voce specifica (cui: campo di collegamento corrisponde al secondo argomento di get-rss-entry). Esamina la sequenza lenta restituita da get-rss-feeds. La valutazione di ciascun articolo richiede una richiesta HTTP attraverso la rete per scaricare un nuovo feed RSS. Per ridurre al minimo il numero di richieste http, è importante esaminare la sequenza una per una e fermarsi non appena c'è una corrispondenza.

Ecco il codice:

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

entry-with-url restituisce una sequenza di partite pigro o una sequenza vuota se non v'è alcuna corrispondenza.

Ho provato questo e sembra funzionare correttamente (valutare un URL di feed alla volta). Ma sono preoccupato che da qualche parte, in qualche modo comincerà a comportarsi in modo "grosso" e inizierà a valutare 32 feed alla volta. So che c'è un modo per avoid chunky behavior as discussed here, ma non sembra nemmeno essere richiesto in questo caso.

Sto usando lazy seq non in modo idiomatico? Ciclo/ricorrenza sarebbe una soluzione migliore?

+0

sembra che una sequenza è solo "Chunked" se si utilizzano le varie funzioni chunk in 'clojure.core' e/o la sequenza implementa il' IChunk' e Interfacce 'IChunkedSeq'. Attualmente (in 1.4.0), questi non sono documentati. – noahlz

+0

quale versione del clojure stai usando? –

+0

Sto usando Clojure v1.4 –

risposta

3

A seconda della vaghezza di Chunking sembra poco saggio come si menziona sopra. Esplicitamente "un chunking" nei casi in cui è davvero necessario non essere suddivisi in blocchi è anche saggio, perché se in un altro momento il codice cambia in un modo che lo blocca, le cose non si interromperanno. In un'altra nota, se sono necessarie azioni sequenziali, gli agenti sono un ottimo strumento che è possibile inviare le funzioni di download a un agente, quindi verranno eseguite una alla volta e solo una volta indipendentemente da come si valuta la funzione. Ad un certo punto potresti voler pmap la tua sequenza e poi anche un-chunking non funzionerà anche se l'utilizzo di un atomo continuerà a funzionare correttamente.

+2

Potresti approfondire questo argomento con uno schizzo di codice di esempio? Intendi agenti invece di atomi? – noahlz

+0

vuoi dire agente piuttosto che atomo qui? perché le funzioni fornite per lo scambio! sarà ritentato – noisesmith

+0

s/atom/agent/g scusate per quello. Le mie dita tradiscono il mio cervello e premono i tasti sbagliati ... risolti. –

5

Seqs pigri sono non sempre suddivisi in pezzi - dipende da come vengono prodotti.

Ad esempio, la ss pigro prodotto da questa funzione non è Chunked:

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

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

Ma molte funzioni integrate in altri Clojure producono seguenti del regolamento provvisorio divisi in blocchi per motivi di prestazioni (ad es range)

+1

È molto importante aggiungere che 'map' e' filter' possono entrambi produrre seq suddivisi in blocchi. Miscelare effetti collaterali e pigrizia è una ricetta per insetti sottili. I trasduttori aiutano qui. –

11

Sei diritto di essere preoccupato. Il tuo get-rss-entry chiamerà infatti lo entry-with-url più del necessario se il parametro feeds è una raccolta che restituisce seq suddivisi in blocchi. Ad esempio se feeds è un vettore, map funzionerà su interi blocchi alla volta.

Questo problema si rivolge direttamente a Fogus' gioia di Clojure, con la funzione seq1 definita nel capitolo 12:

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

si potrebbe usare questo diritto in cui si sa che si desidera possibile la più pigrizia, a destra prima di chiamare entry-with-url:

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

Grazie mille. A proposito, ho appena finito il libro e il mio gioco Clojure è passato al livello successivo. Non vedo l'ora per la versione aggiornata. –

+0

Vale la pena sottolineare che questa chiamata non eseguita a 'seq1' deve essere eseguita alla fonte_. Ad esempio, se stai ricevendo una sequenza lenta da 'map' su una sequenza bocciata, non sei fortunato:' map' guarderà al futuro indipendentemente da ciò che fai. – Thom