Как мне избежать <l120> поведения chunking для ленивых секций, которые я хочу использовать для короткого замыкания?
У меня длинная, ленивая последовательность, которую я хочу уменьшить и проверить лениво. Как только два последовательных элемента не являются =
(или каким-либо другим предикатом) друг к другу, я хочу прекратить использовать список, который стоит дорого. Да, это звучит как take-while
, но читайте дальше.
Я хотел написать что-то простое и элегантное, как это (притворяясь, что every?
работает как reduce
):
(every? = (range 100000000))
Но это не работает лениво, и поэтому он висит на бесконечных секундах. Я обнаружил, что это работает почти так, как я хотел:
(apply = (range 100000000))
Однако я заметил, что чередование последовательности приводит к созданию лишних, ненужных элементов, которые создаются и тестируются. По крайней мере, это то, что я думаю, это то, что происходит в следующем фрагменте кода:
;; 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)))
Я нашел обходное решение, используя take-while
и count
, чтобы проверить количество принятых элементов, но это довольно громоздко.
Должен ли я вежливо предложить Rich Hickey, чтобы он правильно сочетал короткое замыкание reduce
и every?
, или я пропустил какой-то очевидный способ, который уже существует?
РЕДАКТИРОВАТЬ:. Два добродушных человека опубликовали решения для избежания чередования по ленивым последовательностям, но как мне избежать chunking при выполнении apply
, который, кажется, потребляет в chunked группах из четырех?
РЕДАКТИРОВАТЬ № 2: Как отмечает Стюарт Сьерра, я обнаружил, что это не на самом деле. Он просто действует нормально, поэтому я назову это закрытым и дам ему ответ. Я включил небольшую функцию в отдельный ответ, чтобы сделать уменьшающуюся часть проблемы, для тех, кто заинтересован.
Ответы
Ответ 1
ИСПРАВЛЯЕМОЕ ДВАЖДЫ: Простейший способ отключить ленивую последовательность:
(defn unchunk [s]
(when (seq s)
(lazy-seq
(cons (first s)
(unchunk (next s))))))
Первая версия опущена (when ...
, поэтому она вернула бесконечную последовательность nil после окончания входной последовательности.
Вторая версия использовала first
вместо seq
, поэтому она остановилась на нуле.
RE: ваш другой вопрос: "Как избежать блокировки при выполнении заявки, которая, по-видимому, потребляет в разбитых группах по четыре":
Это связано с определением =
, которое при задании последовательности аргументов заставляет первый 4:
(defn =
;; ... other arities ...
([x y & more]
(if (= x y)
(if (next more)
(recur y (first more) (next more))
(= y (first more)))
false)))
Ответ 2
Взгляд в clojure.core в определении apply делает очевидным, почему он разбивается на группы из четырех, когда apply
используется с бесконечной последовательностью. Reduce
не замыкается, либо... поэтому я оставил свое собственное решение:
(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)))
Затем, используя Stuart unchunk (с дополнительным and
)
(defn unchunk [s]
(lazy-seq
(cons (first s)
(and (next s)
(unchunk (next s))))))
Я получаю:
(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
Если это сработает и для вас, измените это.
EDIT: модифицированная версия Стюарта unchunk после его редактирования, вероятно, предпочтительнее той, что была в этом более раннем сообщении.
Ответ 3
Я нашел этот пост, поражая ограничение по сроку 4clojure
и я нашел другой способ избежать 32-кусков:
;; add another dummy sequence parameter to the map:
(apply = (map #(do (prn %2) %) (range) (range)))
Более высокие формальности карты не кажутся чередующимися последовательностями (clojure 1.5)
Вам нужно что-то сделать со вторым параметром, так что
явное объяснение этого может быть лучше:
(apply = (map (fn [i _] (prn i) i) (range) (range)))
Это не так хорошо, как другие решения, но может быть полезно для быстрого и грязного
использует, например, тестирование "это медленное из-за chunking?".
Что касается apply
, вы можете использовать partition
для получения пар из последовательности и = them:
(every? #(apply = %) (partition 2 1
(map (fn [i _] (prn i) i) (range) (range))))
Хотя reducep
выглядит тоже полезно.
PS. Я не хочу создавать впечатление, что чередующаяся последовательность медленнее, это не так.
Моя проблема в том, что тестовый сценарий 4clojure называет "первым" в моей функции генерации seq
диапазон значений, поэтому функция chunking означает, что я делаю 32 раза больше работы.
(PPS. Мой код все еще слишком медленный)