Come evitare il comportamento suddivisione in blocchi di Clojure per seguenti del regolamento provvisorio pigri che voglio corto circuito?

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

Domanda

Ho una lunga sequenza pigro che voglio ridurre e prova pigramente. Appena due elementi sequenziali non sono = (o qualche altro predicato) gli uni agli altri, voglio smettere di consumare la lista, che è costoso da produrre. Sì, questo suona come take-while, ma leggere oltre.

Volevo scrivere qualcosa di semplice ed elegante come questo (finta per un momento che funziona come every? reduce):

(every? = (range 100000000))

Ma che non funziona pigramente e così si blocca su infinite seguenti del regolamento provvisorio. Ho scoperto che questo funziona quasi come volevo:

(apply = (range 100000000))

Tuttavia, ho notato che la sequenza suddivisione in blocchi è stato conseguente, elementi aggiuntivi non necessari in fase di creazione e testato. Almeno, questo è ciò che penso che questo è ciò che accade nel seguente frammento di codice:

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

ho trovato una soluzione utilizzando take-while e count per controllare il numero di elementi tratti, ma che è piuttosto ingombrante.

dovrebbe Ho gentilmente suggerisco di Rich Hickey che poteva fare qualche combinazione di reduce e cortocircuito every? correttamente, o mi sto perdendo qualche modo ovvio che esiste già?

EDIT:? Due persone gentili postato soluzioni per evitare suddivisione in blocchi sulle sequenze pigri, ma come devo fare per evitare la suddivisione in blocchi quando si fa il apply, che sembra essere consumare in gruppi divisi in blocchi di quattro

Modifica # 2: Come Stuart Sierra note e ho scoperto in modo indipendente, questo non è in realtà la suddivisione in blocchi. E 'solo applicare agire normalmente, quindi mi chiamo questo chiuso e gli do la risposta. Ho incluso una piccola funzione in una risposta separata per fare la parte reduce'ing del problema, per coloro che sono interessati.

È stato utile?

Soluzione

CORRETTO DUE VOLTE: Un modo più semplice per un-pezzo una sequenza pigro:

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

Prima versione (when ... omesso quindi restituito un seguenti infinita di di nil dopo la sequenza di ingresso è conclusa.

Seconda versione first usato al posto di seq così si è fermato sul pari a zero.

RE: la tua altra domanda, "Come è possibile evitare la suddivisione in blocchi quando si fa la applicano, che sembra essere consumare in gruppi divisi in blocchi di quattro" :

Ciò è dovuto alla definizione di =, che, data una sequenza di argomenti, forze il primo 4:

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

Altri suggerimenti

Guardando in clojure.core alla definizione di applicare rende evidente il motivo per cui è stato Chunking in gruppi di quattro, quando apply viene utilizzato con una sequenza infinita. Reduce non si corto circuito, o ... quindi mi sono lasciato scrivendo la mia soluzione:

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

Quindi, utilizzando unchunk di Stuart (con un and extra)

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

I ottenere:

(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

Se funziona anche per voi, MOD questo in su.

Modifica : versione modificata di Stuart di unchunk dopo la sua modifica è probabilmente preferibile a quella in questo post in precedenza.

Ho trovato questo post mentre colpisce un limite di tempo su un problema 4clojure e ho trovato un altro modo per evitare i 32-pezzi:

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

Le forme Arity superiori di mappa non sembrano utilizzare sequenze divisi in blocchi (clojure 1.5)

Si deve fare qualcosa con il secondo parametro, in modo da essere esplicito a tale proposito potrebbe essere migliore:

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

Questo non è così pulito come le altre soluzioni, ma potrebbe essere buono per una rapida e sporca usi, come il test "è questo lento a causa della suddivisione in blocchi?".

Per quanto riguarda apply, si potrebbe usare partition per ottenere coppie dalla sequenza e li =:

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

Anche se reducep sembra utile.

PS. Non voglio dare l'impressione che la sequenza Chunked è più lento, non lo è. Il mio problema è che il banco di prova 4clojure chiama "prima" sulla mia funzione generatrice ss over un intervallo di valori, così chunking mezzi faccio 32 volte il lavoro. (PPS. Il mio codice è ancora troppo lento)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top