Как lazy-seq накапливает результат?

Вот реализация функции 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))))))))
  ;; ...
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
142
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Специальная интерпретация 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

P.S.

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

(seq nil) => nil

P.P.S 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.

Настоящие (cons nil '(1)) ;; => (nil 1) и (cons 1 nil) ;; => (1).

Sam R. 26.10.2018 19:01

Другие вопросы по теме