Come evitare il comportamento suddivisione in blocchi di Clojure per seguenti del regolamento provvisorio pigri che voglio corto circuito?
-
25-09-2019 - |
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.
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)