Как преобразовать ленивую последовательность в неленивую в Clojure

95

Я пробовал в Clojure следующее, ожидая возврата класса неленивой последовательности:

(.getClass (doall (take 3 (repeatedly rand))))

Однако это все равно возвращается clojure.lang.LazySeq. Я предполагаю, что doallэто оценивает всю последовательность, но возвращает исходную последовательность, поскольку она все еще полезна для мемоизации.

Итак, каковы идиоматические средства создания неленивых последовательностей из ленивых?

Тим Клемонс
источник
Я удивлен, что никто не спросил, почему вас беспокоит фактический тип возвращаемого значенияdoall
tar
Вы можете преобразовать в вектор:(vec (take 3 (repeatedly rand)))
Крис

Ответы:

161

doallэто все, что тебе нужно. Просто потому, что seqтип has LazySeqне означает, что он ожидает оценки. Lazy seqкэширует свои результаты, поэтому все, что вам нужно сделать, это seqодин раз пройтись с ленивым (как это doallпроисходит), чтобы заставить все это и, таким образом, сделать его неленивым. seqэто не заставит всю коллекцию , чтобы оценить.

Рич Хикки
источник
2
Я изменил это на принятый ответ. В связи с этим, какими средствами вы можете определить, оценивался ли LazySeq ранее?
Тим Клемонс,
10
Я думаю, ты просто позвонишь realized?.
toofarsideways 01
1
Вероятно, должна быть realizeоперация для сопоставления realized?.
Реут
Все это очень хорошо. Но поскольку некоторые функции, например contains?, не заботятся о том, реализовали ли вы ленивую последовательность или нет, это отвечает на заданный конкретный вопрос, но в меньшей степени на заголовок вопроса.
Matanster
75

В некоторой степени это вопрос таксономии. Ленивая последовательность - это всего лишь один тип последовательности, такой как список, вектор или карта. Итак, ответ, конечно, «это зависит от того, какой тип неленивой последовательности вы хотите получить:
выберите из:

  • бывшая ленивая (полностью оцененная) ленивая последовательность (doall ... )
  • список для последовательного доступа (apply list (my-lazy-seq)) OR (into () ...)
  • вектор для последующего произвольного доступа (vec (my-lazy-seq))
  • карта или набор, если у вас есть какое-то специальное назначение.

Вы можете выбрать любой тип последовательности, который больше всего соответствует вашим потребностям.

Артур Ульфельдт
источник
Это лучший ответ.
Felipe
4
Принятый ответ технически правильный, но этот ответ был для меня наиболее полезным. Я пытался сопоставить функцию с вектором, а затем передать результаты в файл, и даже после вызова doall файл содержал «clojure.lang.LazySeq@address» вместо содержимого последовательности. Вызов vec на возвращенной карте значений дал мне то, что мне нужно было выплюнуть в файл.
Джесси Розалия
1
@JesseRosalia Приятно знать, что единственный ответ Рича Хики во всем SO был технически правильным. ;-)
Фил Купер
(vec (my-lazy-seq))не так хорош в следующих ситуациях: (vec (json/parse-string "{\"foo\":\"bar\"}")) ;; => [["foo" "bar"]]Т.к. cheshireвыбирает создание lazy-seq из(json/parse-string)
codeasone
Для смягчения вышеуказанного было использование нетерпеливого(json/parse-string-strict)
codeasone
22

Этот Богатый парень, кажется, знает свое закрытие и абсолютно прав.
Но я думаю, что этот фрагмент кода, используя ваш пример, может быть полезным дополнением к этому вопросу:

=> (realized? (take 3 (repeatedly rand))) 
false
=> (realized? (doall (take 3 (repeatedly rand)))) 
true

Действительно, тип не изменился, но реализация изменилась.

Питер
источник
2
Однако стоит отметить, что вам не нужно принудительно realized?возвращать всю последовательность true. Например(let [r (range) r? (realized? r)] (doall (take 1 r)) [r? (realized? r)]) => [false true]
Alex Coventry
22
Этот богатый парень: D haha
nimrod
10
@nimrod :) каламбур, однако, должен был быть в "hís clojure".
Питер
10
Для тех, кто не знает, «Богатый парень» изобрел Clojure.
erturne
1
@AlexCoventry возвращает ваш пример[true true]
7

Я наткнулся на это сообщение в блоге о том, что он doallне рекурсивен. Для этого я обнаружил, что первый комментарий в посте помог. Что-то вроде:

(use 'closure.walk)
(postwalk identity nested-lazy-thing)

Я нашел это полезным в модульном тесте, где я хотел принудительно выполнить оценку некоторых вложенных приложений, mapчтобы вызвать состояние ошибки.

leeor
источник
5
(.getClass (into '() (take 3 (repeatedly rand))))
Ступито
источник
3
Это ужасная идея. Он меняет входную последовательность на обратную.
amalloy
3
Конечно, в этом случае реверсирование ввода не имеет значения, поскольку это всего лишь 3 случайных числа .... :-)
mikera 03