Как мне избежать <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. Мой код все еще слишком медленный)