Clojure: Использование потока без удержания головы. Это правильно?

Я хочу разместить где-нибудь ленивую последовательность, чтобы предоставлять данные по мере необходимости. Я знаю, что мне нужно избегать держаться за начало последовательности. Я придумал следующее решение, я что-то пропустил?

(defn headless [s] 
  (let [a (atom s)] 
    (fn 
      ([]  (let [s @a r (first s)] 
             (swap! a rest) r)) 
      ([n] (let [s @a rs (take n s)] 
             (swap! a #(drop n %)) rs))))) 

Пример использования, этот простой генератор просто дает натуральные числа.

(def nums (headless (iterate inc 0)))

(nums 5)
; (0 1 2 3 4)

(nums)
;5

Обновление: «тест» должен использовать dorun, а не doall. См. решение lgrapenthin

(не слишком реалистичный) тест с

(doall (map #(nums %) (repeat 20)))

произошел сбой после 5 минут использования всех 4 ядер с исключением (пространство кучи Java OutOfMemoryError)


person Falko    schedule 11.09.2013    source источник


Ответы (1)


Ваш код работает.

Эта форма:

(doall (map #(nums %) (repeat 20)))

будет генерировать бесконечное количество (числа 20) и никогда не вернется. Вместо этого вы можете использовать dorun, чтобы удалить сгенерированные (nums 20) и не хранить их в памяти. Однако он не вернется, потому что (repeat 20) генерирует бесконечную ленивую последовательность.

Чуть более читабельная версия headless

(defn headless [s] 
  (let [a (atom s)] 
    (fn 
      ([]  (let [s @a]
             (swap! a rest)
             (first s))
      ([n] (let [s @a]
             (swap! a (partial drop n))
             (take n s)))))))
person Leon Grapenthin    schedule 11.09.2013
comment
Ах, так doall понимает последовательность, dorun просто запускает ее. Спасибо. - person Falko; 11.09.2013