2010-07-27 4 views
5

A partir d'une collection de chaînes comme:Cloisonnement clojure avec une collection paresseuse de chaînes

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

L'objectif est d'extraire hors un certain nombre de caractères de la tête de la collection de chaînes, la génération d'un groupe cloisonné de chaînes. Ceci est le comportement souhaité:

(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)) 

Cependant, en utilisant l'évaluation des forces se joindre de la collection, ce qui provoque des problèmes de mémoire lorsqu'ils traitent avec de très grandes collections de cordes. Mon cas particulier d'utilisation génère des sous-ensembles de chaînes à partir d'une collection paresseuse générée par l'analyse d'un grand fichier d'enregistrements délimités:

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

et construit sur le travail de this previous question. J'ai essayé des combinaisons de réduction, de partition et de jointure, mais je ne peux pas trouver la bonne incantation pour tirer les caractères de la tête de la première corde et évaluer paresseusement les chaînes suivantes si nécessaire. Merci beaucoup pour toute idée ou pointeur.

Répondre

5

Vous ne savez pas exactement ce que vous allez faire, mais ce qui suit fait ce que votre premier exemple fait, et le fait paresseusement.

étape par étape pour plus de clarté:

 
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)) 

Tous ensemble:

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

Pas besoin d'aplatir, juste concatuer les séquences de caractères en utilisant mapcat: (partition-tous les 3 (mapcat seq str-coll)) –

+0

ataggart et Jürgen, merci beaucoup pour les solutions: mappage à un seq était exactement ce que j'étais disparu. Surmonter cet obstacle m'a fait réaliser que la partition n'était pas aussi paresseuse que j'avais espéré. Alors que chaque partition est fournie de manière paresseuse, les composants individuels de chaque partition ne le sont pas; donc le partitionnement du fichier initial aux délimiteurs ne fournit pas les chaînes paresseuses désirées qui alimentent cela. –

+0

@ Jürgen: mapcat n'est pas fainéant (il utilise apply), donc pourquoi je ne l'ai pas utilisé. –

1

EDIT: tout ce que j'ÉCRITES A TORT

Lorsqu'une fonction d'un var-arg est appliqué avec un seq plus long que le nombre d'arguments discrets, le reste du seq est passé comme var-arg (voir RestFn.applyTo).

À Jürgen: Je suis stupide. Vous êtes intelligent. J'avais tort. Tu avais raison. Tu es le meilleur. Je suis le pire. Tu es très beau. Je ne suis pas attirant.

Ce qui suit est un compte rendu de mon idiotie ...


En réponse au commentaire de Jürgen Hötzel.

mapcat n'est pas totalement paresseux car apply n'est pas paresseux dans l'évaluation du nombre d'arguments à appliquer. En outre, apply ne peut pas être paresseux car les fonctions doivent être invoquées avec un nombre discret d'arguments. Actuellement, si le nombre d'arguments dépasse 20, les arguments restants sont déversés dans un tableau, donc non-paresseux.

Donc regardant la source 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))) 

Si nous élargissons l'évaluation à l'aide de l'exemple, la apply intérieure évaluerait à:

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

qui est bien puisque les str-coll doesn pas complètement réalisé, mais alors l'extérieur apply évaluerait à:

 
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) 

Notez que le apply externe applique n arguments à concat, un pour chaque chaîne dans l'original str-coll. Maintenant, il est vrai que le résultat de concat est paresseux, et chaque arg est lui-même paresseux, mais vous devez encore réaliser toute la longueur de str-coll pour obtenir ces n seqs paresseux. Si str-coll a 1000 chaînes, alors concat obtiendra 1000 arguments, et toutes les 1000 chaînes devront être lues dans le fichier et en mémoire avant que concat puisse être appelé.


Pour les incroyants, une démonstration de la seq réalisant le comportement d'appliquer:

 
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 

Et une démonstration que varargs ne sont pas paresseux:

 
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 

Bien que le contrepoint, que les œuvres suivantes me déroute:

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

Il n'y a pas de "get all n seqs paresseux", concat est invoqué avec une "liste d'arguments paresseux".Vous pouvez le vérifier dans notre exemple pratique en définissant la collection de chaînes à une liste paresseuse infinie: (def str-coll (répéter « ABCD »)) Et puis il suffit de prendre une partie du résultat: (prendre 10 (partition-tous les 3 (mapcat seq str-coll))) –

+0

S'il vous plaît voir la modification au bas de mon message pour plus de preuve de ma réclamation. –

+0

En outre, concat n'est pas invoqué avec une "liste d'arguments paresseux". Il n'y a rien comme ça. Toute invocation de fonction se produit en passant un nombre discret d'arguments à une fonction. Vous pouvez le voir dans les méthodes invoke de IFn ici: http://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/IFn.java Que la fonction déclare, par exemple, [& coll] , signifie simplement que les args sont enveloppés dans un seq (non-paresseux). –

Questions connexes