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

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

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

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

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


person Tim Clemons    schedule 29.10.2009    source источник
comment
Я удивлен, что никто не спросил, почему вас беспокоит фактический тип возвращаемого значения doall   -  person tar    schedule 01.11.2017
comment
Вы можете преобразовать в вектор: (vec (take 3 (repeatedly rand)))   -  person Kris    schedule 23.01.2018


Ответы (5)


doall - это все, что вам нужно. Тот факт, что seq имеет тип LazySeq, не означает, что он ожидает оценки. Ленивые seqs кэшируют свои результаты, поэтому все, что вам нужно сделать, это пройти по ленивому seq один раз (как это делает doall), чтобы заставить все это и, таким образом, сделать его неленивым. seq не принудительно оценивает всю коллекцию.

person Rich Hickey    schedule 29.10.2009
comment
Я изменил это на принятый ответ. Кстати, каким образом вы можете определить, оценивался ли LazySeq ранее? - person Tim Clemons; 29.10.2009
comment
Я полагаю, вы просто позвоните realized?. - person toofarsideways; 01.02.2012
comment
Вероятно, должна быть realize операция для сопоставления realized?. - person Reut Sharabani; 01.02.2017
comment
Все это очень хорошо. Но поскольку некоторым функциям, таким как contains?, все равно, реализовали ли вы ленивую последовательность или нет, это отвечает на заданный конкретный вопрос, но в меньшей степени на заголовок вопроса. - person matanster; 16.05.2017

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

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

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

person Arthur Ulfeldt    schedule 29.10.2009
comment
Это лучший ответ. - person Felipe; 25.07.2014
comment
Принятый ответ технически правильный, но этот ответ оказался для меня наиболее полезным. Я пытался сопоставить функцию с вектором, а затем передать результаты в файл, и даже после вызова doall файл содержал clojure.lang.LazySeq@address вместо содержимого последовательности. Вызов vec на возвращенной карте значений дал мне то, что мне нужно было выплюнуть в файл. - person Jesse Rosalia; 24.11.2015
comment
@JesseRosalia Приятно знать, что единственный ответ Рича Хики во всем SO был технически правильным. ;-) - person Phil Cooper; 16.03.2016
comment
(vec (my-lazy-seq)) не так хорош в следующих ситуациях: (vec (json/parse-string "{\"foo\":\"bar\"}")) ;; => [["foo" "bar"]] Поскольку cheshire выбирает создание lazy-seq из (json/parse-string) - person codeasone; 07.02.2018
comment
Смягчением вышеуказанного было использование нетерпеливого (json/parse-string-strict) - person codeasone; 07.02.2018

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

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

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

person Peter    schedule 06.09.2013
comment
Однако стоит отметить, что вам не нужно заставлять всю последовательность realized? возвращать true. Например. (let [r (range) r? (realized? r)] (doall (take 1 r)) [r? (realized? r)]) => [false true] - person Alex Coventry; 21.04.2014
comment
Этот богатый парень: Ха-ха - person Julian Krispel-Samsel; 26.08.2014
comment
@nimrod :) каламбур, однако, должен был быть в его закрытии. - person Peter; 04.11.2014
comment
Для тех, кто не знает, Богатый изобрел Clojure. - person erturne; 16.11.2014
comment
@AlexCoventry ваш пример возвращает [true true] - person ; 24.01.2016
comment
Ха, поведение должно быть изменилось с тех пор, как я написал этот комментарий. - person Alex Coventry; 09.02.2016

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

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

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

person leeor    schedule 29.08.2015

person    schedule
comment
Это ужасная идея. Он меняет входную последовательность на обратную. - person amalloy; 16.08.2011
comment
Конечно, в этом случае реверсирование ввода не имеет значения, поскольку это всего лишь 3 случайных числа .... :-) - person mikera; 03.01.2013