2010-07-27 3 views
5

Beginnend mit einer Sammlung von Zeichenkette wie:Partitioning in clojure mit einer faulen Sammlung von Strings

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

Das Ziel ist es, eine bestimmte Anzahl von Zeichen aus dem Kopf der Strings Sammlung zu extrahieren aus, eine partitionierten Gruppierung zu erzeugen von Saiten. Dies ist das gewünschte Verhalten:

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

jedoch mit Kräften Auswertung der gesamten Sammlung beitreten, die Speicherprobleme verursacht, wenn mit sehr großen Sammlungen von Zeichenketten handelt. Mein spezieller Anwendungsfall Teilmengen von Zeichenkette aus einer faulen Sammlung erzeugt Erzeugen von einer großen Datei von begrenzten Aufzeichnungen Parsen:

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

und baut auf der Arbeit von this previous question. Ich habe Kombinationen von reduce, partition und join versucht, kann aber nicht die richtige Beschwörungsformel finden, um Zeichen vom Kopf der ersten Saite zu ziehen und nachfolgende Saiten nach Bedarf zu bewerten. Vielen Dank für Anregungen oder Hinweise.

Antwort

5

Nicht ganz sicher, was Sie für, aber das Folgende tun, was Ihr erstes Beispiel tut, und es so träge tut.

Schritt-für-Schritt für Klarheit:

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

Alle zusammen jetzt:

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

Keine Notwendigkeit zu flachen, nur die Zeichenfolgen mit mapcat: (Partition-alle 3 (mapcat seq str-coll)) –

+0

ataggart und Jürgen, vielen Dank für die Lösungen: Mapping zu einem Seq war genau das, was ich war fehlt. Die Überwindung dieser Hürde ließ mich erkennen, dass die Partitionierung nicht so träge wirkte, wie ich es mir erhofft hatte. Während jede Partition auf eine faule Weise bereitgestellt wird, sind die einzelnen Komponenten jeder Partition nicht; Das Partitionieren der Anfangsdatei bei Trennzeichen stellt daher nicht die gewünschten Lazy-Strings bereit, die in dieses Feld eingefügt werden. –

+0

@ Jürgen: mapcat ist nicht faul (es verwendet apply), weshalb ich es nicht verwendet habe. –

1

EDIT: ALLES ich geschrieben habe UNRECHT

Wenn eine Funktion mit einem var-arg wird mit einer Sequenz angewendet, die länger ist als die Anzahl diskreter Argumente, der Rest der Sequenz wird als var-arg übergeben (siehe RestFn.applyTo).

Zu Jürgen: Ich bin dumm. Du bist schlau. Ich habe mich geirrt. Du hattest Recht. Du bist der beste. Ich bin der Schlechteste. Du siehst sehr gut aus. Ich bin nicht attraktiv.

Das Folgende ist eine Abspeicherung meiner Idiotie ...


Als Reaktion auf Jürgen Hötzel Kommentar.

mapcat ist nicht vollständig faul, weil apply ist nicht faul bei der Beurteilung der Anzahl der anzuwendenden Argumente. Ferner kann apply nicht faul sein, weil Funktionen mit einer diskreten Anzahl von Argumenten aufgerufen werden müssen. Derzeit, wenn die Anzahl der Argumente 20 überschreitet, werden die verbleibenden Argumente in ein Array ausgegeben, daher nicht faul.

sucht also an der Quelle für 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))) 

Wenn wir die Auswertung aus am Beispiel die inneren apply würde beurteilen zu erweitern:

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

die seit dem str-coll doesn ist in Ordnung werde nicht vollständig realisiert, aber dann würde der äußere apply zu bewerten:

 
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) 

Beachten Sie, dass die äußere apply gilt n Argumente zu concat, eine für jede Zeichenfolge in der ursprünglichen str-coll. Nun, es ist wahr, dass das Ergebnis von concat faul ist, und jeder arg ist selbst faul, aber Sie müssen noch die volle Länge von str-coll zu bekommen, um diese n Lazy Seqs zu bekommen. Wenn str-coll 1000 Zeichenfolgen hat, dann erhält concat 1000 Argumente, und alle 1000 Zeichenfolgen müssten aus der Datei ausgelesen und in den Speicher geladen werden, bevor concat aufgerufen werden kann.


Für The Unbelievers, eine Demonstration der Seq-Realisierung Verhalten gelten:

 
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 

Und eine Demonstration, dass varargs sind nicht faul:

 
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 

Obwohl als Kontrapunkt, dass die folgenden Arbeiten verwirren mich:

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

Es gibt keine "alle diese n Lazy Seqs". concat wird mit einer "Lazy Argument Liste" aufgerufen.Sie können die String-Sammlung praktischer dies in unserem Beispiel überprüfen, um eine unendlich faul Liste setzen: (def str-coll (Wiederholung „abcd“)) Und dann nehmen Sie nur einen Teil des Ergebnisses: (nehmen 10 (Partition-alle 3 (mapcat seq str-coll))) –

+0

Bitte beachten Sie die Änderung am Ende meines Beitrags für weitere Beweise für meine Behauptung. –

+0

concat wird auch nicht mit einer "faulen Argumentliste" aufgerufen. Das gibt es nicht. Der Aufruf aller Funktionen erfolgt durch Übergeben einer diskreten Anzahl von Argumenten an eine Funktion. Sie können dies in den Aufrufmethoden von IFn hier sehen: http://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/IFn.java Die Funktion deklariert zB [& coll] , bedeutet einfach, dass die Argumente in einem (nicht faulen) Seq. –