2010-07-27 13 views
5

a partire da un insieme di stringhe come:partizionamento in clojure con una collezione pigra di stringhe

(def str-coll ["abcd" "efgh" "jklm"]) 

L'obiettivo è quello di estrarre fuori un numero specifico di caratteri dalla testa della collezione di stringa, generando un raggruppamento partizionato di stringhe. Questo è il comportamento desiderato:

(use '[clojure.contrib.str-utils2 :only (join)]) 
(partition-all 3 (join "" str-coll)) 

((\a \b \c) (\d \e \f) (\g \h \j) (\k \l \m)) 

Tuttavia, utilizzando la valutazione unire le forze di tutta la collezione, che provoca problemi di memoria quando si tratta di grandi collezioni di stringhe. Il mio caso specifico uso sta generando sottoinsiemi di stringhe da una collezione pigro generata da analisi di un file di grandi dimensioni di record delimitati:

(defn file-coll [in-file] 
    (->> (line-seq (reader in-file)) 
    (partition-by #(.startsWith ^String % ">")) 
    (partition 2)))) 

e sta costruendo sul lavoro da this previous question. Ho provato combinazioni di riduzione, partizione e join ma non riesco a trovare il giusto incantesimo per estrarre i caratteri dalla prima stringa e valutare pigramente le stringhe successive in base alle esigenze. Grazie mille per qualsiasi idea o suggerimento.

risposta

5

Non sei sicuro di cosa stai andando, ma quanto segue fa quello che fa il tuo primo esempio, e lo fa pigramente.

Step-by-step per chiarezza:

 
user=> (def str-coll ["abcd" "efgh" "jklm"]) 
#'user/str-coll 
user=> (map seq str-coll) 
((\a \b \c \d) (\e \f \g \h) (\j \k \l \m)) 
user=> (flatten *1) 
(\a \b \c \d \e \f \g \h \j \k \l \m) 
user=> (partition 3 *1) 
((\a \b \c) (\d \e \f) (\g \h \j) (\k \l \m)) 

Tutti insieme ora:

 
(->> str-coll 
    (map seq) 
    flatten 
    (partition 3)) 
+1

Non c'è bisogno di appiattire, basta concatenare le sequenze di caratteri usando mapcat: (partition-all 3 (mapcat seq str-coll)) –

+0

ataggart e Jürgen, grazie mille per le soluzioni: mappare a un seq era esattamente quello che ero mancante. Superare quell'ostacolo mi portò a realizzare che partizione non agiva pigramente come speravo. Mentre ogni partizione viene fornita in modo lazy, i singoli componenti di ogni partizione non lo sono; quindi il partizionamento del file iniziale ai delimitatori non fornisce le stringhe pigro desiderate che si alimentano in questo. –

+0

@ Jürgen: mapcat non è pigro (usa apply), quindi perché non l'ho usato. –

1

EDIT: tutto quello che ho scritto è stato sbagliato

Quando una funzione con un var-arg viene applicato a un seq più lungo del numero di argomenti discreti, il resto del seq viene passato come var-arg (vedere RestFn.applyTo).

A Jürgen: Sono stupido. Sei intelligente. Mi sbagliavo. Avevi ragione. Sei il migliore. Sono il peggiore. Sei molto bello. Non sono attraente

La seguente è una traccia della mia idiozia ...


risposta al commento di Jürgen Hötzel.

mapcat non è completamente pigro perché apply non è pigro nel valutare il numero di argomenti da applicare. Inoltre, apply non può essere pigro perché le funzioni devono essere invocate con un numero discreto di argomenti. Attualmente se il numero di args supera 20, gli argomenti rimanenti vengono scaricati in un array, quindi non pigri.

Così guardando la fonte per mapcat:

 
(defn mapcat 
    "Returns the result of applying concat to the result of applying map 
    to f and colls. Thus function f should return a collection." 
    {:added "1.0"} 
    [f & colls] 
    (apply concat (apply map f colls))) 

Se ci espandiamo la valutazione utilizzando l'esempio, l'interno apply sarebbe valutato come:

 
user=> (map seq str-coll) 
((\a \b \c \d) (\e \f \g \h) (\j \k \l \m)) 

che va bene in quanto i str-coll doesn essere pienamente realizzato, ma poi l'esterno apply valuterebbe:

 
user=> (concat '(\a \b \c \d) '(\e \f \g \h) '(\j \k \l \m)) 
(\a \b \c \d \e \f \g \h \j \k \l \m) 

Si noti che l'esterno apply applica n argomenti a concat, uno per ogni stringa nell'originale str-coll. Ora, è vero che il risultato di concat è pigro, e ogni argomento è esso stesso pigro, ma è comunque necessario realizzare l'intera lunghezza di n lazy seqs. Se str-coll ha 1000 stringhe, allora concat otterrà 1000 arg e tutte le stringhe 1000 dovranno essere lette dal file e in memoria prima che sia possibile chiamare concat.


Per The Unbelievers, una dimostrazione del comportamento seq-realizzativa applica:

 
user=> (defn loud-seq [] (lazy-seq (println "HELLO") (cons 1 (loud-seq)))) 
#'user/loud-seq 
user=> (take 3 (loud-seq)) ; displaying the lazy-seq realizes it, thus printing HELLO 
(HELLO 
HELLO 
1 HELLO 
1 1) 
user=> (do (take 3 (loud-seq)) nil) ; lazy-seq not realized; no printing of HELLO 
nil 
user=> (do (apply concat (take 3 (loud-seq))) nil) ; draw your own conclusions 
HELLO 
HELLO 
HELLO 
nil 

E una dimostrazione che varargs non sono pigri:

 
user=> (defn foo [& more] (type more)) 
#'user/foo 
user=> (foo 1 2 3 4) 
clojure.lang.ArraySeq 
user=> (apply foo (repeat 4 1)) 
clojure.lang.Cons 

Sebbene come contrappunto, che i seguenti lavori mi sconcertano:

 
user=> (take 10 (apply concat (repeat [1 2 3 4]))) 
(1 2 3 4 1 2 3 4 1 2) 
+0

Non c'è" get tutti quei n seq pigri ". concat viene invocato con una" lista di argomenti pigri ".È possibile controllare questo nel nostro esempio pratico impostando la raccolta stringa in un infinita lista pigro: (DEF str-Coll (ripetere "ABCD")) E poi basta prendere una parte del risultato: (prendere 10 (partition-all 3 (mapcat seq str-coll))) –

+0

Vedere la modifica in fondo al mio post per ulteriori prove del mio reclamo. –

+0

Inoltre, concat non viene invocato con una "lista di argomenti pigri". Non c'è niente del genere. L'invocazione di tutte le funzioni avviene passando un numero discreto di argomenti ad una funzione. Puoi vedere questo nei metodi invoke di IFn qui: http://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/IFn.java Che la funzione dichiara, ad es. [& Coll] , significa semplicemente che gli argomenti sono racchiusi in un seq (non pigro). –

Problemi correlati