How to create and read from a stream of random characters?

126 views
Skip to first unread message

GS

unread,
Jan 11, 2009, 9:35:40 PM1/11/09
to Clojure
Hi,

For the purposes of testing another function (not discussed here), I
wrote a function to generate random strings. This is what I ended up
with after some trial and error.

(defn generate-data [size maxlength]
; Returns a collection of 'size random strings, each at most
'maxlength chars.
(let [alphabet "abcdefghijklmnopqrstuvwxyz"
rand-letter (fn [_] (nth alphabet (rand-int 26)))
rand-stream (map rand-letter (iterate inc 0))
rand-string (fn [n] (apply str (take n rand-stream)))]
(map (fn [_] (rand-string (rand-int maxlength)))
(range size))))

; test
(generate-data 25 19)


The output of the testing function is something like ("gr", "gry",
"gr", "g", "gry"). That is, is always _takes_ from the same sequence
of random characters.

Is there a way I might modify the above code so rand-stream is indeed
a stream (with new random data each time) instead of a sequence (where
every _take_ starts from the beginning)?

Of course, I'd love to see other people's approach to implementing
'generate-data, but I'm also curious about the idea of streams
themselves.

Cheers,
Gavin [second post]

Chouser

unread,
Jan 12, 2009, 2:50:50 AM1/12/09
to clo...@googlegroups.com
On Sun, Jan 11, 2009 at 9:35 PM, GS <gsin...@gmail.com> wrote:
>
> Hi,
>
> For the purposes of testing another function (not discussed here), I
> wrote a function to generate random strings. This is what I ended up
> with after some trial and error.
>
> (defn generate-data [size maxlength]
> ; Returns a collection of 'size random strings, each at most 'maxlength chars.
> (let [alphabet "abcdefghijklmnopqrstuvwxyz"
> rand-letter (fn [_] (nth alphabet (rand-int 26)))
> rand-stream (map rand-letter (iterate inc 0))
> rand-string (fn [n] (apply str (take n rand-stream)))]
> (map (fn [_] (rand-string (rand-int maxlength)))
> (range size))))
>
> ; test
> (generate-data 25 19)
>
> The output of the testing function is something like ("gr", "gry",
> "gr", "g", "gry"). That is, is always _takes_ from the same sequence
> of random characters.
>
> Is there a way I might modify the above code so rand-stream is indeed
> a stream (with new random data each time) instead of a sequence (where
> every _take_ starts from the beginning)?

That generates a single rand-stream and then takes various amounts
from it in your 'map'. To get a new random sequence each time, you'd
have to move the definition of rand-stream into your 'map' fn so that
each item of the map gets its own random stream.

Here's one way to do that:

(defn generate-data [size maxlength]
(for [i (range size)]
(apply str (take (rand-int maxlength)
(repeatedly #(char (+ (int \a) (rand-int 26))))))))

But since this is returning a lazy stream (as your original did),
there's not much value in passing in the size:

(defn seq-of-rand-strings [maxlength]
(repeatedly (fn []
(apply str (take (rand-int maxlength)
(repeatedly #(char (+ (int \a) (rand-int 26)))))))))

user=> (take 3 (seq-of-rand-strings 10))
("kae" "xwuwyp" "xa")

--Chouser

GS

unread,
Jan 12, 2009, 3:38:58 AM1/12/09
to Clojure

> (defn seq-of-rand-strings [maxlength]
>   (repeatedly (fn []
>     (apply str (take (rand-int maxlength)
>                      (repeatedly #(char (+ (int \a) (rand-int 26)))))))))
>
> user=> (take 3 (seq-of-rand-strings 10))
> ("kae" "xwuwyp" "xa")

Thanks Chouser. I learned some useful and interesting things from
your reply. I was unaware of 'char and 'repeatedly.

Gavin

Brian Doyle

unread,
Jan 12, 2009, 4:06:44 PM1/12/09
to clo...@googlegroups.com
Chouser's solution works well, but I found that you can end up
with an empty string sometimes or never getting the maxlength
that you passed in.

1:2 user=> (take 3 (seq-of-rand-strings 10))
("ngene" "" "dwlbzndqx")

I added an inc before calling the take and that clears things up.


(defn seq-of-rand-strings [maxlength]
    (repeatedly (fn []
       (apply str (take (inc (rand-int maxlength))

                         (repeatedly #(char (+ (int \a) (rand-int 26)))))))))

Reply all
Reply to author
Forward
0 new messages