I originally abandoned clj-generators because it introduces significant
performance overhead (and an extra thread), which I think makes it
unrealistic for generating lazy sequences in practice.

Perhaps switching to dar.async would remove the overhead:

https://github.com/dar-clojure/async

On Tue, Mar 14, 2017 at 10:59 PM Alan Thompson <clooj...@gmail.com> wrote:

> A recent post to StackOverflow
> <http://stackoverflow.com/questions/42798706/clojure-clojurescript-elegant-way-to-generate-a-deck-of-cards/42801711#42801711>
> asked for clarification on different ways of generating a deck of cards.
> The OP had used nested `for` with `flatten` and `map-indexed` but thought
> there must be a better (clearer & more concise) solution.  In addition to
> the first two solutions offered, this problem also shows an example of when
> Python-style *generator functions* with a *yield* statement may also be
> considered. Pasted below is the example:
>
> ---------------------------------------------------------
> For a third take on this problem, you can use the generator/yield style of
> programming popular in Python. This solution makes use of the *lazy-gen* &
> *yield* combination from the Tupelo library
> <https://github.com/cloojure/tupelo#generator-functions-for-lazy-sequences-a-la-python>
> :
>
>
> (ns tst.clj.core
>   (:use clojure.test tupelo.test)
>   (:require  [tupelo.core :as t]  ))
> (t/refer-tupelo)
>
> (defn new-deck []
>   (lazy-gen
>     (let [id (atom 0)]
>       (doseq [suit [:spade :heart :diamond :club]]
>         (doseq [rank [:2 :3 :4 :5 :6 :7 :8 :9 :10 :jack :queen :king :ace]
> ]
>           (yield {:id (swap! id inc) :suit suit :rank rank}))))))
>
> (pprint (new-deck))
>
> ({:suit :spade, :rank :2, :id 1}
>  {:suit :spade, :rank :3, :id 2}
>  {:suit :spade, :rank :4, :id 3}
>  {:suit :spade, :rank :5, :id 4}
>  {:suit :spade, :rank :6, :id 5}
>  {:suit :spade, :rank :7, :id 6}
>  {:suit :spade, :rank :8, :id 7}
>  {:suit :spade, :rank :9, :id 8}
>  {:suit :spade, :rank :10, :id 9}
>  {:suit :spade, :rank :jack, :id 10}
>  {:suit :spade, :rank :queen, :id 11}
>  {:suit :spade, :rank :king, :id 12}
>  {:suit :spade, :rank :ace, :id 13}
>  {:suit :heart, :rank :2, :id 14}
>  {:suit :heart, :rank :3, :id 15}
>  {:suit :heart, :rank :4, :id 16}
>  {:suit :heart, :rank :5, :id 17}
> <snip>
>  {:suit :club, :rank :10, :id 48}
>  {:suit :club, :rank :jack, :id 49}
>  {:suit :club, :rank :queen, :id 50}
>  {:suit :club, :rank :king, :id 51}
>  {:suit :club, :rank :ace, :id 52})
>
> I just had to use keywords for the card names since using :rank 13 for the
> ace (or is it the king?) is just wrong...(shudder)...on so many levels! ;)
> Note that it is legal for a keyword in Clojure to consist of only digits.
>
> Since *yield* is putting the individual items on the output queue, we
> don't need to return a sequence from for and can just use doseq. Also, we
> don't need to worry about a multi-variate for, nor about using flatten,
> concat, or mapcat constructs. Use of the atom for id is as simple as it
> gets, although you could build the whole thing out of loop/recur instead of
> the atom & 2 doseq forms if you really wanted to be "pure".
>
> Under the covers *lazy-gen/yield* uses core.async to create an output
> stream with a default buffer size of 32 (modeled after lazy chunking
> in clojure.core).
>
> While for and map-indexed solutions work fine for this example, sometimes
> it may be clearer to the reader to be extra explicit about the looping
> constructs, etc. Also, if there were other operations in the inner loop
> before & after the yield statement, it might be awkward to force the
> solution into a map or for style solution.
>
> Enjoy!
>
> Alan
>
> P.S.  I noticed after searching that Alex Engelberg worked on something
> similar a few years ago. This version is slightly different from his
> (hopefully improved!), although I did switch some symbols to gensyms after
> seeing his implementation.  :)
>
> P.P.S.  Before the purists complain about the use of mutable state &
> imperative loops, please remember that *for some problems* (not all!)
> this may yield a simpler solution with fewer mental stack frames required.
> Also, the solution is a pure function overall as seen from the outside,
> since it returns a non-side-effecting lazy-sequence, and neither the state
> atom nor the imperative loops escape the function body. This is similar to
> the implementation of many functions in clojure.core itself.
>
>
> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clojure@googlegroups.com
> Note that posts from new members are moderated - please be patient with
> your first post.
> To unsubscribe from this group, send email to
> clojure+unsubscr...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en
> ---
> You received this message because you are subscribed to the Google Groups
> "Clojure" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to clojure+unsubscr...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.
>

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
--- 
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to clojure+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to