У меня есть длинная, ленивая последовательность, которую я хочу уменьшить и проверить лениво , Как только два последовательных элемента не =
(или некоторый другой предикат) друг к другу, я хочу прекратить использовать список, который является дорогим для производства. Да, это звучит как take-while
, но читайте дальше.
Я хотел написать что-то простое и элегантное, как это (притворяясь на минуту, что каждый?
работает как ] уменьшить
):
(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
для проверки количества элементы взяты, но это довольно громоздко.
Должен ли я вежливо предложить Ричу Хикки, чтобы он сделал какую-то комбинацию , чтобы уменьшить
и каждый?
правильное короткое замыкание правильно, или я пропускаю некоторые очевидный путь, который уже существует?
РЕДАКТИРОВАТЬ: Два добрых человека опубликовали решения, позволяющие избежать разброса ленивых последовательностей, но как мне избежать чанкинга при применении
, которое, по-видимому, потребляется в чанкованных группах по четыре?
РЕДАКТИРОВАТЬ # 2: Как отмечает Стюарт Сьерра, и я обнаружил, что это не так т на самом деле кусаться. Он просто действует как обычно, поэтому я назову это закрытым и дам ему ответ. Я включил небольшую функцию в отдельный ответ, чтобы выполнить уменьшающую часть проблемы для тех, кому это интересно.
ДВАЖДЫ ИСПРАВЛЕНО: Более простой способ разбить ленивую последовательность на части:
(defn unchunk [s]
(when (seq s)
(lazy-seq
(cons (first s)
(unchunk (next s))))))
Первая версия опущена (когда ...
, поэтому она вернула бесконечную последовательность из нулей после завершения входной последовательности.
Вторая версия использовала сначала
вместо seq
], поэтому он остановился на nil.
RE: ваш другой вопрос, «как мне избежать разбиения на фрагменты при применении, которое, кажется, потребляет фрагменты групп по четыре» :
Это связано с определением =
, которое при заданной последовательности аргументов заставляет первые 4:
(defn =
;; ... other arities ...
([x y & more]
(if (= x y)
(if (next more)
(recur y (first more) (next more))
(= y (first more)))
false)))
Если посмотреть в 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)))
Затем, используя unchunk Стюарта (с дополнительными и
)
(defn unchunk [s]
(lazy-seq
(cons (first s)
(and (next s)
(unchunk (next s))))))
I get:
(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
Если это работает и для вас, модифицируйте это.
РЕДАКТИРОВАТЬ : модифицированная версия unchunk Стюарта после его редактирования, вероятно, предпочтительнее той, что была в этом более раннем посте.