2013-08-06 9 views
5

Podczas przetwarzania każdego elementu w seq zwykle używam first i rest. Jednak spowoduje to, że lazy-seq straci swoje "lenistwo", wywołując argument o nazwie seq. Moje rozwiązanie polegało na używaniu (first (take 1 coll)) i (drop 1 coll) na swoim miejscu podczas pracy z lazy-seq s, i chociaż myślę, że drop 1 jest w porządku, nie bardzo lubię zadzwonić pod numer first i take, aby uzyskać pierwszy element.Idiomatyczny sposób na uzyskanie pierwszego elementu leniwego seq w clojure

Czy istnieje bardziej idiomatyczny sposób na zrobienie tego?

Odpowiedz

10

W docstrings dla first i rest powiedzieć, że funkcje te nazywają seq na swoich argumentów, aby przekazać ideę, że nie trzeba zadzwonić seq się przechodząc w seqable kolekcji, która nie jest sama w sobie nast, jak mówią , wektor lub zestaw. Na przykład,

(first [1 2 3]) 
;= 1 

nie będzie działać, jeśli first nie zadzwonił seq na swojej argumentacji; zamiast tego trzeba byłoby powiedzieć, że byłoby to niewygodne.

Zarówno take, jak i drop również wywołują w swoich argumentach seq, w przeciwnym razie nie można ich wywołać na wektorach itp., Jak wyjaśniono powyżej. W rzeczywistości dotyczy to wszystkich standardowych kolekcji seq - te, które nie nazywają się seq, są bezpośrednio zbudowane na komponentach niższego poziomu.

W żaden sposób nie osłabia to lenistwa leniwych seqs. Wymuszenie/wykonanie, które dzieje się w wyniku połączenia first/rest, jest najmniejszą możliwą do uzyskania żądanym wynikiem. (Ile to zależy od rodzaju argumentu, jeśli w rzeczywistości nie jest leniwy, nie ma dodatkowej realizacji związanej z rozmową first, jeśli jest częściowo leniwy - to znaczy, porwany - będzie trochę dodatkowych realizacja (do 32 elementów początkowych zostanie obliczonych od razu), jeśli jest w pełni leniwy, tylko pierwszy element zostanie obliczony.)

Najwyraźniej first, po przejściu leniwego seq, musi wymusić realizację swojego pierwszego elementu - - o to chodzi. rest jest rzeczywiście nieco leniwy, ponieważ faktycznie nie wymusza realizacji części "odpoczynku" seq (to w przeciwieństwie do next, która jest zasadniczo równoważna z (seq (rest ...))). Fakt, że zmusza pierwszy element do realizacji, aby mógł go natychmiast pominąć, jest świadomym wyborem projektu, który unika niepotrzebnego nakładania warstw leniwych obiektów i trzymania główki oryginalnego seq; możesz powiedzieć coś takiego, jak (lazy-seq (rest xs)), aby odroczyć nawet tę początkową realizację, kosztem utrzymania się na xs, dopóki nie zostanie zrealizowane opakowanie lazy seq.

+0

Dzięki za to. Sądzę, że to różnica między tym, co za dużo, a tym, co w pełni leniwe, rzuciła mnie. W szczególności, gdy pracowałem najpierw z leniwym seqem, myślę, że wszystko, nad czym pracowałem, musiało powodować problemy podczas dzielenia, więc utknąłem po tym. – robertjlooby

+0

Tak, chunking może prowadzić do nieoczekiwanych rezultatów, jeśli ktoś o tym zapomni. 'take' używa zarówno' first' jak i 'rest' w swojej implementacji, natomiast' drop' jest wbudowane w 'rest', więc nie pomagają.Unchunking jest możliwy, ale tylko w tym sensie, że można dokonać transformacji ułożonych na wierzchu fragmentów kawałków, aby ocenić jeden element na raz; leżące u jego podstawy seq zawsze zrealizuje jego porcje w całości. (Sposób na zrobienie tego to owijanie seq w niezakładane seq, być może wyprodukowane z '(reify clojure.lang.IScheq ...)' z oryginalnym seq w zamknięciu.) –

+0

Jesteś pewny 'take'/'drop' również nazywa się' seq'? Po prostu wypróbowanie go w repl i wywołanie 'class' z' (range) ',' (take 1 (range)) ',' (drop 1 (range)) 'all daje' clojure.lang.LazySeq'. Wywołanie z '(reszta (zakres))', '(seq (zakres)) daje' clojure.lang.ChunkedCons'. – robertjlooby

Powiązane problemy