Jak przekonwertować sekwencję leniwą na nieleniwą w Clojure

95

Wypróbowałem następujące czynności w Clojure, spodziewając się, że zwrócona zostanie klasa nieleniwej sekwencji:

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

Jednak to wciąż powraca clojure.lang.LazySeq. Domyślam się, że doallocenia całą sekwencję, ale zwraca oryginalną sekwencję, ponieważ jest nadal przydatna do zapamiętania.

Więc jaki jest idiomatyczny sposób tworzenia nieleniwej sekwencji z leniwej?

Tim Clemons
źródło
Jestem zaskoczony, że nikt nie zapytał, dlaczego martwisz się o rzeczywisty typ zwracanej wartościdoall
tar
Możesz przekonwertować na wektor:(vec (take 3 (repeatedly rand)))
Kris

Odpowiedzi:

161

doallto wszystko czego potrzebujesz. Tylko dlatego, że seqtyp has LazySeqnie oznacza, że ​​oczekuje na ocenę. Leniwy zapisuje w seqpamięci swoje wyniki, więc wszystko, co musisz zrobić, to przejść seqraz leniwego (tak doallsamo jak ), aby wymusić to wszystko, a tym samym uczynić go nie leniwym. seqnie nie zmusi całą kolekcję do oceny.

Rich Hickey
źródło
2
Zmieniłem to na zaakceptowaną odpowiedź. A propos, za pomocą jakich środków możesz określić, czy LazySeq został wcześniej oceniony?
Tim Clemons
10
Wierzę, że po prostu zadzwoń realized?.
toofarsideways
1
Prawdopodobnie powinna istnieć odpowiednia realizeoperacja realized?.
Reut Sharabani
To wszystko jest bardzo miłe. Ale ponieważ niektóre funkcje, takie jak contains?nie dbają o to, czy zdałeś sobie sprawę z leniwej sekwencji, czy nie, to odpowiada na konkretne pytanie, które zostało zadane, ale mniej na tytuł pytania.
matanster
75

Do pewnego stopnia jest to kwestia taksonomii. leniwa sekwencja to tylko jeden typ sekwencji, podobnie jak lista, wektor czy mapa. Odpowiedź brzmi więc oczywiście „to zależy od tego, jakiego typu nie leniwą sekwencję chcesz uzyskać:
Wybierz spośród:

  • była leniwa (w pełni oceniona) leniwa sekwencja (doall ... )
  • lista dostępu sekwencyjnego (apply list (my-lazy-seq)) OR (into () ...)
  • wektor do późniejszego dostępu swobodnego (vec (my-lazy-seq))
  • mapę lub zestaw, jeśli masz jakiś specjalny cel.

Możesz mieć dowolny typ sekwencji, który najbardziej odpowiada Twoim potrzebom.

Arthur Ulfeldt
źródło
To najlepsza odpowiedź.
Felipe,
4
Przyjęta odpowiedź jest technicznie poprawna, ale ta odpowiedź była dla mnie najbardziej przydatna. Próbowałem zmapować funkcję na wektorze, a następnie wyplułem wyniki do pliku i nawet po wywołaniu doall plik zawierał „clojure.lang.LazySeq@address” zamiast zawartości sekwencji. Wezwanie vec na mapie wartości zwróciło mi to, co musiałem wypluć do pliku.
Jesse Rosalia
1
@JesseRosalia Dobrze wiedzieć, że jedyna odpowiedź Richa Hickeya w całej sprawie była poprawna technicznie. ;-)
Phil Cooper
(vec (my-lazy-seq))nie jest takie przyjemne w sytuacjach takich jak poniżej: (vec (json/parse-string "{\"foo\":\"bar\"}")) ;; => [["foo" "bar"]]Ponieważ cheshiredecyduje się na produkcję lazy-seq z(json/parse-string)
codeasone
Łagodzenie powyższego polegało na gorliwym użyciu(json/parse-string-strict)
codeasone
22

Wydaje się, że ten bogaty facet zna swój strój i ma całkowitą rację.
Ale myślę, że ten fragment kodu, używając twojego przykładu, może być przydatnym uzupełnieniem tego pytania:

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

Rzeczywiście typ się nie zmienił, ale realizacja zmieniła się

Piotr
źródło
2
Warto jednak zauważyć, że nie musisz zmuszać całej sekwencji realized?do powrotu true. Np.(let [r (range) r? (realized? r)] (doall (take 1 r)) [r? (realized? r)]) => [false true]
Alex Coventry
22
Ten bogaty facet: D haha
Julian Krispel-Samsel
10
@nimrod :) gra słów miała być jednak w "hís clojure".
Peter
10
Dla tych, którzy nie wiedzą, „bogacz” wymyślił Clojure.
erturne
1
@AlexCoventry Twój przykład zwraca[true true]
7

Natknąłem się na tym wpisie na blogu o doalltym, że nie jest rekurencyjny. W tym celu stwierdziłem, że pierwszy komentarz w poście załatwił sprawę. Coś w rodzaju:

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

Znalazłem to przydatne w teście jednostkowym, w którym chciałem wymusić ocenę niektórych zagnieżdżonych aplikacji, mapaby wymusić stan błędu.

leeor
źródło
5
(.getClass (into '() (take 3 (repeatedly rand))))
stupito
źródło
3
To okropny pomysł. Odwraca sekwencję wejściową.
amalloy
3
Oczywiście w tym przypadku odwrócenie wejścia nie robi różnicy, ponieważ są to tylko 3 losowe liczby .... :-)
mikera