Comment puis-je éviter le comportement Chunking de Clojure pour seqs paresseux que je veux un court-circuit?

StackOverflow https://stackoverflow.com/questions/3407876

Question

J'ai une longue séquence, paresseux que je veux réduire et tester paresseusement. Dès que deux éléments successifs ne sont pas = (ou un autre prédicat) à l'autre, je veux arrêter de consommer la liste, ce qui est coûteux à produire. Oui, cela ressemble à take-while, mais lire plus loin.

Je voulais écrire quelque chose de simple et élégant comme celui-ci (en faisant semblant d'une minute qui every? fonctionne comme reduce):

(every? = (range 100000000))

Mais cela ne fonctionne pas paresseusement et il est en attente de seqs infinies. J'ai découvert que cela fonctionne presque comme je voulais:

(apply = (range 100000000))

Cependant, je remarque que la séquence a été Chunking sorte que des éléments supplémentaires, inutiles étant créés et testés. Au moins, ce que je pense que c'est ce qui se passe dans le bit de code suivant:

;; Displays chunking behavior in groups of four on my system and prints 1 2 3 4
(apply = (map #(do (println %) %) (iterate inc 1)))

;; This prints 0 to 31
(apply = (map #(do (println %) %) (range)))

J'ai trouvé une solution à l'aide take-while et count pour vérifier le nombre d'éléments pris, mais qui est assez lourd.

Dois-je suggérer poliment Rich Hickey qu'il fasse une combinaison de reduce et every? court-circuit correctement, ou suis-je manque une certaine façon évidente qui existe déjà?

EDIT: Deux personnes aimables a affiché des solutions pour éviter Chunking sur les séquences paresseux, mais comment puis-je éviter Chunking quand faire le apply, qui semble consommer dans les groupes chunked de quatre

EDIT # 2: Comme notes Stuart Sierra et j'ai découvert de façon indépendante, cela ne fait Chunking. Il Appliquons simplement agir normalement, donc je vais appeler cette ferme et lui donner la réponse. J'ai inclus une petite fonction dans une réponse distincte pour faire la partie reduce'ing du problème, pour ceux qui sont intéressés.

Était-ce utile?

La solution

CORRIGE DEUX FOIS: Une façon simple de non-fragment d'une séquence paresseux:

(defn unchunk [s]
  (when (seq s)
    (lazy-seq
      (cons (first s)
            (unchunk (next s))))))

La première version omise (when ... il a renvoyé un seq infini de néant des années après la séquence d'entrée terminée.

Deuxième version utilisée first au lieu de seq il est arrêté sur zéro.

RE: votre autre question, "comment puis-je éviter Chunking lorsque vous faites l'appliquer, ce qui semble consommer dans les groupes chunked de quatre" :

Ceci est dû à la définition de =, qui, lorsqu'elle est administrée une séquence d'arguments, force le premier 4:

(defn =
  ;; ... other arities ...
  ([x y & more]
   (if (= x y)
     (if (next more)
       (recur y (first more) (next more))
       (= y (first more)))
     false)))

Autres conseils

Recherche dans clojure.core à la définition d'appliquer, il est évident que les raisons pour lesquelles il a été Chunking en groupes de quatre ans quand apply est utilisé avec une suite infinie. Reduce ne pas court-circuiter, soit ... donc je laisse écrire ma propre solution:

(defn reducep
  "Like reduce, but for use with a predicate. Short-circuits on first false."
  ([p coll]
     (if-let [s (seq coll)]
       (reducep p (first s) (next s))
       (p)))
  ([p val coll]
     (if-let [s (seq coll)]
       (if-let [v (p val (first s))]
         (recur p (first s) (next s))
         false)
       true)))

Ensuite, en utilisant la unchunk de Stuart (avec un and supplémentaire)

(defn unchunk [s]
  (lazy-seq
   (cons (first s)
         (and (next s)
              (unchunk (next s))))))

Je reçois:

(reducep = (map #(do (print %) %) (unchunk (range)))) ;; Prints 01, returns false
(reducep = (map #(do (print %) %) (repeat 20 1))) ;; returns true
(reducep = (map #(do (print %) %) (unchunk [0 0 2 4 5]))) ;; Returns false
(reducep = (map #(do (print %) %) (unchunk [2 2 2 2 2]))) ;; returns true

Si cela fonctionne pour vous aussi, mod cela.

EDIT : la version modifiée de Stuart de unchunk après son édition est sans doute préférable à celui de ce poste plus tôt.

J'ai trouvé ce poste tout en frappant une limite de temps sur un problème de 4clojure et j'ai trouvé une autre façon d'éviter les 32-morceaux:

;; add another dummy sequence parameter to the map:
(apply = (map #(do (prn %2) %) (range) (range)))

Les formes de arity plus de carte ne semblent pas utiliser des séquences chunked (clojure 1.5)

Vous devez faire quelque chose avec le second paramètre, afin d'être explicite à ce sujet pourrait être mieux:

(apply = (map (fn [i _] (prn i) i) (range) (range)))

Ce n'est pas aussi propre que les autres solutions, mais peut-être bon pour rapide et sale utilise, comme le test « est-ce lent à cause de Chunking? ».

En ce qui concerne apply, vous pouvez utiliser partition pour obtenir des paires de la séquence et les =:

(every? #(apply = %) (partition 2 1
    (map (fn [i _] (prn i) i) (range) (range))))

Bien que reducep semble trop utile.

PS. Je ne veux pas donner l'impression que la séquence chunked est plus lente, ce n'est pas. Mon problème est que le cas de test 4clojure appelle « première » sur ma fonction de génération seq sur une plage de valeurs, de sorte que des moyens chunking je fais 32 fois le travail. (PPS. Mon code est encore trop lent)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top