добавление метаданных в отложенную последовательность
-
20-08-2019 - |
Вопрос
Когда я пытаюсь добавить метаданные в бесконечную ленивую последовательность в Clojure, я получаю переполнение стека, и если я удаляю метаданные, то это работает просто отлично.Почему добавление with-meta
макрос прерывает ленивый seq?
Сначала создайте бесконечную последовательность очень хорошего числа:
(defn good [] (lazy-seq (cons 42 (good)))) user> (take 5 (good)) (42 42 42 42 42)
Затем добавьте некоторые метаданные к каждому из экземпляров lazy-seq:
(defn bad [] (lazy-seq (cons 42 (with-meta (bad) {:padding 4})))) user> (take 5 (bad)) java.lang.StackOverflowError (NO_SOURCE_FILE:0) [Thrown class clojure.lang.Compiler$CompilerException]
Попробуйте переместить метаданные на один уровень выше:
(defn also-bad [] (with-meta (lazy-seq (cons 42 (also-bad))) {:padding 4})) user> (take 5 (foo)) java.lang.StackOverflowError (NO_SOURCE_FILE:0) [Thrown class clojure.lang.Compiler$CompilerException]
Вот пример метаданных для конечной последовательности:
(defn also-works [] (lazy-seq (cons 4 (with-meta () {:a 5})))) user> (also-works) (4) user> (meta (rest (also-works))) {:a 5} user>
Решение
Потому что a LazySeq
оценивает его тело, как только вы вызываете withMeta
на LazySeq
.Вы теряете свою лень.
public final class LazySeq extends Obj implements ISeq, List{
...
public Obj withMeta(IPersistentMap meta){
return new LazySeq(meta, seq());
}
...
}
seq()
оценивает тело отложенного seq, если оно еще не было оценено.Ваш приведенный выше код продолжает вызывать with-meta
при последовательных ленивых проверках, которые оценивают их все до тех пор, пока стек не взорвется.Я не думаю, что в настоящее время существует какой-либо способ добавить метаданные в lazy seq, не заставляя его оценивать свое тело.