2
\$\begingroup\$

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.

\$\endgroup\$
1
\$\begingroup\$

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)))
\$\endgroup\$
  • 1
    \$\begingroup\$ Using generic functions is the right way here, of course. Thanks for reminding me ;-) \$\endgroup\$ – Martin Buchmann May 28 at 11:56
2
\$\begingroup\$

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:

Link to github

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;
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.