Как Lazy-Seq накапливает результат? - PullRequest
0 голосов
/ 26 октября 2018

Вот реализация из функции partition в clojurescript. Другие методы удалены для простоты.

Мне трудно понять, как lazy-seq накапливает результат. В конце есть when, который, если я правильно понимаю, вернет nil, если тест ложен. Куда это nil перейдет в следующей итерации lazy-seq?

(defn partition
  "Returns a lazy sequence of lists of n items each, at offsets step
  apart. If step is not supplied, defaults to n, i.e. the partitions
  do not overlap. If a pad collection is supplied, use its elements as
  necessary to complete last partition up to n items. In case there are
  not enough padding elements, return a partition with less than n items."
  ;; ...
  ([n step coll]
     (lazy-seq
       (when-let [s (seq coll)]
         (let [p (take n s)]
           (when (== n (count p))
             (cons p (partition n step (drop step s))))))))
  ;; ...

1 Ответ

0 голосов
/ 26 октября 2018

Специальная интерпретация nil

cons - это специальная форма (т.е. это не функция, а встроенный компилятор).cons знает, что nil означает «данные больше не поступают».

(cons 7 nil)   => (7)
(cons 7 '())   => (7)
(cons 7  [])   => [7]

Итак, если либо when-let, либо when не пройден, возвращается nil, и мы имеем что-то вроде (cons 7 nil).Следовательно, ленивая последовательность прекращается (nil отбрасывается), и в этот момент она эквивалентна простому списку.


Возвращается nil

Ваш вопрос удивилмне!Я не думал, что это сработает, но вот код:

(defn odd->nil [it]
  (if (odd? it)
    nil
    it))

(defn down-from
  "Count down from N to 1"
  [n]
  (lazy-seq
    (when (pos? n)
      (cons (odd->nil n) (down-from (dec n))))))

(down-from 5) => (nil 4 nil 2 nil)

Итак, мы видим, что есть большая разница между nil, являющимся первым или вторым аргументом cons.Если nil является первым аргументом, он добавляется в начало списка как обычно.Если nil является вторым аргументом, он (молча) преобразуется в пустой список, а в результате получается список из 1 элемента:

(cons nil [99])  => (nil 99)   ; works like normal
(cons  99  nil)  => (99)       ; creates a 1-elem list
(cons nil  nil)  => (nil)      ; for completeness

PS

Примечаниеесть небольшое противоречие с seq, так как у нас есть:

(seq nil) => nil

PPS rest против next

Я никогда не использую next, так как я надеваюне люблю тихие преобразования в nil:

(next [1]) => nil
(next [])  => nil
(next nil) => nil

Я предпочитаю использовать rest, так как это даст мне пустой список, как я ожидаю:

(rest [1]) => ()
(rest [])  => ()
(rest nil) => ()

Я могузатем напишите такой тест:

  (let [remaining (rest some-seq) ]
    (when-not (empty remaining)      ; says what it means
       ....more processing.... ))

Мне не нравятся предположения о бесшумных преобразованиях:

(when (next some-seq)        ; silently converts [] => nil
  ....more processing.... )  ; & relies on nil <=> false

Одна последняя вещь

Вы можете бытьинтересует небольшая доработка под названием lazy-cons , описанная здесь .Я думаю, что он немного проще, чем оригинальный lazy-seq.

(defn lazy-countdown [n]
  (when (<= 0 n)
    (lazy-cons n (lazy-countdown (dec n)))))

(deftest t-all
  (is= (lazy-countdown  5) [5 4 3 2 1 0] )
  (is= (lazy-countdown  1) [1 0] )
  (is= (lazy-countdown  0) [0] )
  (is= (lazy-countdown -1) nil ))

Он также имеет двоюродного брата, эмулирующего функции генератора в стиле Python .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...