During implementation and testing of different small hobby projects I often reach the point where I need (at least I like it more) some random data beyond the usual foo-bar strings or lists. Random here does not mean really mathematical random data but just something that looks random to humans.
Therefore I came up with the following:
;;;; random.lisp
;;; Time-stamp: <2019年05月27日 14:53:38 m.buchmann>
;;;
;;; A short sketch of some random data generating functions.
;;;
(ql:quickload "alexandria")
;; * Choosing a random element from a given list.
(defun random-draw (list &aux (len (length list)) (pos (random len)))
"Returns a random element from LIST."
(nth pos list))
;;; * Generating a random element
(defun random-element (type &key (radix 10) (case :up))
"Returns a random element of type :digit (depending on :radix) or
:character (case given by :case :up, :down or :both). Limited to
7bit ASCII characters from A to z."
(let ((char-range (ecase case
(:up (alexandria:iota 26 :start 65))
(:down (alexandria:iota 26 :start 97))
(:both (append (alexandria:iota 26 :start 65)
(alexandria:iota 26 :start 97))))))
(ecase type
(:digit (random radix))
(:character (code-char (random-draw char-range))))))
(defun random-list (len &key (type :digit) (radix 10) (case :up))
"Returns a list of length LEN filled with random elements of TYPE :digit or :character."
(loop :for i from 0 below len
:collect (random-element type :radix radix :case case)))
(defun random-string (len &key (case :up) &aux (result (make-array len :element-type 'character)))
"Returns a random string of length LEN and :case (:up, :down or :both)."
(loop :for i :from 0 :below len
:do (setf (aref result i) (random-element :character :case case)))
result)
I did not pack it in a proper package etc. yet because my use case is pretty simple and usually temporary. I was wondering if other people have implemented similar things or if the need I felt was just so individual that no one else really cares about it. I did not find other libraries for this.
I think it could be easily extended to supply random-arrays, hash-tables and so on. Also the character encoding could be improved to deliver more than 7bit ASCII and this in a portable way.
Any comments on the usability, style etc. are gratefully acknowledged.
2 Answers 2
Note that Alexandria defines random-elt
(and whichever
).
My only complain is that random-element
does too much, both digits and characters.
This would be the kind of use cases where I would rely on generic functions:
(defgeneric generate (type &key &allow-other-keys))
(defmethod generate ((type (eql :number/offset)) &key offset length)
(+ offset (random length)))
(defmethod generate ((type (eql :number/around)) &key (origin 0) (length 1.0))
(generate :number/offset
:offset (- origin (/ length 2))
:length length))
(defmethod generate ((type (eql :ascii)) &key case)
(multiple-value-bind (offset length)
(ecase case
(:down (values 97 26))
(:up (values 65 26))
(:both (values 65 52)))
(code-char
(generate :number/offset :offset offset :length length))))
(defmethod generate ((type (eql :digit)) &key radix)
(random radix))
(defmethod generate ((type (eql :choose-from)) &key sequence)
(random-elt sequence))
For example:
(list (generate :ascii :case :up)
(generate :digit :radix 8)
(generate :number/around :origin 0 :length 10)
(generate :choose-from :sequence #(5 6 8)))
-
1\$\begingroup\$ Using generic functions is the right way here, of course. Thanks for reminding me ;-) \$\endgroup\$Martin Buchmann– Martin Buchmann2019年05月28日 11:56:30 +00:00Commented May 28, 2019 at 11:56
Within python there is the faker package creating "human" data. It's often used for testing and can easily be extended to your personal needs:
It also has a command line interface. Here is the example from the official documentation:
$ faker address
968 Bahringer Garden Apt. 722
Kristinaland, NJ 09890
$ faker -l de_DE address
Samira-Niemeier-Allee 56
94812 Biedenkopf
$ faker profile ssn,birthdate
{'ssn': u'628-10-1085', 'birthdate': '2008-03-29'}
$ faker -r=3 -s=";" name
Willam Kertzmann;
Josiah Maggio;
Gayla Schmitt;