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

Обновление: "тест" должен использовать дорун, а не доалл. Посмотреть решение от lgrapenthin

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

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

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

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)))))))
Другие вопросы по тегам