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.