Hey Mayank!

What you've done here might appear to work, but it will get you into
trouble when test.check starts to shrink your inputs. When test.check runs
your generators it relies you you using it as your only source of
randomness, and so your use of `rand-int` will cause some problems.

The trick is to use the `bind` function to make a generator which depends
on the value of another generator (in this case, to capture the generated
map so you can call rand-subset with the correct set of keys):

(defn rand-subset
  "Given a collection coll
   it'll generate a random subset."
  [coll]
  (gen/fmap (fn [i] (combo/nth-subset coll i))
            (gen/choose 0 (dec (combo/count-subsets coll)))))

(defn gen-varying-map
  "Given a generator which generates a map,
   it'll randomly select keys from it thus making it
   varying-sized map.
   Note: It can return empty maps as well."
  [map-gen]
  (gen/bind map-gen
            (fn [map]
              (gen/fmap (fn [keyseq]
                          (select-keys map keyseq))
                        (rand-subset (keys map))))))

(gen/sample (gen-varying-map (gen/hash-map
                              "user" (gen/such-that not-empty
gen/string-alpha-numeric)
                              "level" gen/nat
                              "timezone" gen/pos-int)))
=>
({"user" "e", "level" 0}
 {"level" 1}
 {"user" "M1"}
 {"timezone" 2}
 {"user" "2", "level" 2, "timezone" 0}
 {"timezone" 3}
 {"user" "W", "level" 5, "timezone" 0}
 {"timezone" 5}
 {}
 {})

This output appears the same as yours, but it will produce predictable
shrink trees, and thus will shrink more effectively.

Carlo

On 26 July 2015 at 07:10, Mayank Jain <firesof...@gmail.com> wrote:

> Hi,
>
> I would like to generate variable sized map using test.check i.e.
> given any generator which generates a map, it should randomly select-keys
> from it.
>
> Here's what I've come up with so far:
>
>
>> (ns proj.util
>   (:require [clojure.test.check.generators :as gen]
>             [clojure.math.combinatorics :as combo]))
>
> (defn rand-subset
>   "Given a collection coll,
>    it'll generate a random subset."
>   [coll]
>   (->> coll
>        combo/count-subsets
>        rand-int
>        (combo/nth-subset coll)))
>
> (defn gen-varying-map
>   "Given a generator which generates a map,
>    it'll randomly select keys from it thus making it
>    varying-sized map.
>    Note: It can return empty maps as well."
>   [map-gen]
>   (gen/fmap (fn [m]
>               (let [ks (rand-subset (keys m))]
>                 (select-keys m ks)))
>             map-gen))
>
> Here's an example output,
> (gen/sample (gen-varying-map (gen/hash-map
>                               "user" (gen/such-that not-empty
>
> gen/string-alphanumeric)
>                               "level" gen/nat
>                               "timezone" gen/pos-int)))
> =>
> ({"user" "1"}
>  {"user" "l8", "level" 0, "timezone" 1}
>  {"level" 1}
>  {"user" "oA", "timezone" 0}
>  {"level" 2, "timezone" 1}
>  {"level" 5}
>  {"user" "8aP", "level" 5, "timezone" 6}
>  {"user" "035rqi", "level" 7}
>  {"timezone" 4}
>  {"timezone" 2})
>
> My question is, is this the right way? Or is there a better way to do it?
>
> Thanks :)
>
> --
> 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