¿Cómo puedo evitar el comportamiento de fragmentación de Clojure para SEQs perezosos que quiero cortocircuito?

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

Pregunta

I tienen una larga, perezosa secuencia que quiero reducir y prueba de pereza. Tan pronto como dos elementos secuenciales no son = (o algún otro predicado) entre sí, quiero dejar de consumir la lista, que es caro de producir. Sí, esto suena como take-while, pero leer más.

Yo quería escribir algo sencillo y elegante como esta (fingiendo por un minuto que funciona como every? reduce):

(every? = (range 100000000))

Pero eso no funciona con pereza y por lo que se cuelga en SEQs infinitas. Descubrí que esto funciona casi como quería:

(apply = (range 100000000))

Sin embargo, he notado que la secuencia CHUNKING se traduce en elementos adicionales, innecesarios siendo creado y probado. Al menos, esto es lo que creo que esto es lo que ocurre en el siguiente fragmento de código:

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

I encontró una solución usando take-while, y count para comprobar el número de elementos tomados, pero que es bastante engorroso.

¿Debo cortésmente sugiero a Rich Hickey que hacer alguna combinación de reduce y cortocircuito every? correctamente, o me estoy perdiendo de alguna manera obvia de que ya existe?

EDIT:? Dos personas amables publicado las soluciones para evitar la fragmentación de las secuencias perezosos, pero ¿cómo puedo evitar fragmentación al hacer el apply, que parece estar consumiendo en grupos fragmentados de cuatro

editar # 2: Como Stuart Sierra notas y he descubierto de forma independiente, esto no es en realidad la fragmentación. Se aplica sólo actúa normalmente, por lo que voy a llamar a esta cerrado y le dará la respuesta. Incluí una pequeña función en una respuesta por separado a la parte reduce'ing del problema, para aquellos que estén interesados.

¿Fue útil?

Solución

CORREGIDO dos veces: Una forma más sencilla de un-trozo una secuencia perezosa:

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

La primera versión (when ... omitido por lo que devolvió un infinito de la SEC de Nil después de la secuencia de entrada terminado.

Segunda versión utilizada en lugar de first seq por lo que se detuvo en nulo.

RE: su otra pregunta, "¿Cómo puedo evitar fragmentación al hacer la aplican, lo que parece estar consumiendo en grupos fragmentados de cuatro"

Esto es debido a la definición de =, que, cuando se le da una secuencia de argumentos, las fuerzas de la primera 4:

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

Otros consejos

Mirando en clojure.core en la definición de aplicar hace que sea obvia de por qué se ha de fragmentación en grupos de cuatro, cuando apply se utiliza con una secuencia infinita. Reduce hace corto circuito no, tampoco ... así que me quedo escribiendo mi propia solución:

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

A continuación, utilizando unchunk de Stuart (con un and extra)

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

Puedo obtener:

(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 funciona para usted también, mod esto.

Editar : versión modificada de Stuart de unchunk después de su edición es probablemente preferible a la de este post anterior.

He encontrado este post, mientras que golpear un límite de tiempo en un problema 4clojure y me encontré con otra forma de evitar los 32-trozos:

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

Las formas Arity más altos de mapa no aparecen usar secuencias fragmentadas (clojure 1,5)

Hay que hacer algo con el segundo parámetro, por lo que ser explícita acerca de que podría ser mejor:

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

Esto no es tan limpio como las otras soluciones, pero podría ser bueno para una rápida y sucia usos, como la prueba "es lento debido a esta fragmentación?".

En cuanto a apply, se podría utilizar para obtener partition pares de la secuencia y = ellas:

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

A pesar de reducep parece útil también.

PS. No quiero dar la impresión de que la secuencia fragmentada es más lento, no lo es. Mi problema es que el caso de prueba 4clojure llama "primera" en mi función de generación de la SEC sobre un rango de valores, por lo que significa que hacer 32 veces el trabajo fragmentación. (PPS. Mi código es todavía demasiado lento)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top