Copyright © 2004 Gene Michael Stover. All rights reserved. Permission to copy, store, & view this document unmodified & in its entirety is granted.
This is how I wrote my DEF-SERVER-STRUCT macro which was an important part of keeping a CGI/web project on schedule. Without it, I would have had to write much more code by hand, & the project would have required more time.
The source code in the file http://lisp-p.org/desest/def-server-struct.lisp & http://lisp-p.org/desest/test.lisp is released in accordance with the GNU Lesser General Public License. There is a copy of the license agreement at http://lisp-p.org/desest/COPYING.
This article is not covered by the Gnu Lesser General Public License. It is covered by its own copyright notice which is at the beginning of the article.
The source code files are all at http://lisp-p.org/desest/. They are:
I had to write a web application that had several data types in a data bank & allowed the CRUD operations on each of them. CRUD is an acronym for Create Read Update Delete, the operations commonly required on objects in a data bank.
Conceptually, it's a simple application, but if I had gone down the brute force implementation path, I would have needed to write a few CGI programs for each data type, each with one or more HTML templates, marshalling code, data-validation logic, & functions for the data bank. None of that code would have been difficult, but none of it would have been very interesting, either, & most of it would have been almost the same as the rest of it. So I thought up a way to write a simple Lisp program (``macro'' in Lisp terms) that would write all those functions for all those data types.
fixme: Copied this section from ../jmt/. Modify it for the macro at hand.
This section is a brief tutorial. The full reference manual is in Section
.
The first step to using this Mersenne Twister is to load it. You could load it into Lisp with a literal pathname, like this:
> (load "jmt.lisp") ; load from a literal pathname
or you could load from a logical pathname, like this:
;; Assuming cl-library already has some translations
;; defined for it.
> (push '("stover;jason;jmt.lisp" "jmt.lisp")
(logical-pathname-translations "cl-library"))
> (load (translate-logical-pathname
"CL-LIBRARY:STOVER;JASON;JMT.LISP"))
T
The basic function for obtaining pseudoranom numbers from this Mersenne Twister is the MT-RANDOM function. It works like Common Lisp's RANDOM function.
With an integral argument
, MT-RANDOM
returns an
pseudorandom integral number
such that
, like this:
> (loop for i from 1 to 5
collect (mt-random 6))
(0 4 0 5 1)
What computer programmer hasn't played a roll-playing game?1Let's generate the six basic attributes for a character from an old edition of Dungeons & Dragons:
> (defun d6 () (1+ (mt-random 6)))
D6
> (defun roll-attr ()
(+ (d6) (d6) (d6)))
ROLL-ATTR
> (loop for i from 1 to 6 collect (roll-attr))
(4 14 14 11 13 10)
With a floating point argument
, MT-RANDOM
returns a
floating point pseudorandom number
. Again, this is like the Common Lisp
RANDOM
function. Here is an
example:
> (loop for i from 1 to 10
collect (mt-random 1.0))
(0.03372876 0.2431892 0.99524146 0.0033221766
0.6445225 0.6361625 0.89716256 0.35805753
0.6595478 0.07814171)
Like the RANDOM function, MT-RANDOM allows an optional second argument. That second argument must be NIL or an instance of MT-RANDOM-STATE . If it is an MT-RANDOM-STATE , a copy of that state will be saved & used for future calls of MT-RANDOM unless you again use a state argument.
You can make a new MT-RANDOM-STATE with the MAKE-MT-RANDOM-STATE function. Let's make a pseudorandom random state now:
;; Use a PROGN so the random state isn't printed. ;; It's long & not pretty, I don't want to see it. > (progn (setq rs (make-mt-random-state t)) nil) NIL
Save a copy of the new random state so we can compare to it later:
> (progn (setq cs (make-mt-random-state rs)) nil) NIL
Generate a few numbers with the new random state, RS:
> (mt-random 10 rs) 5 > (loop for i from 1 to 3 collect (mt-random 10)) (0 4 8)
By specifying RS again, we can repeat that same sequence of numbers & demonstrate that the MT-RANDOM-STATE found to RS was copied & not altered by MT-RANDOM :
> (mt-random 10 rs) 5 > (loop for i from 1 to 3 collect (mt-random 10)) (0 4 8)
This Mersenne Twister stores its random state in the global variable *MT-RANDOM-STATE* much as the Common Lisp library stores its random state in *RANDOM-STATE* .
When you specify a non-NIL value as the second argument to MT-RANDOM-STATE , that value is bound to *MT-RANDOM-STATE* & used as the random state until you specify another value for it.
You may also use SETQ to bind an instance of MT-RANDOM-STATE to *MT-RANDOM-STATE* .
The *MT-RANDOM-STATE* global variable is specifically initiallized to a pseudorandom value when the library is loaded. Unless you need to recapture a sequence of random numbers or to continue a sequence from another process, it should not be necessary to initialize it specifically. I think this departs from Common Lisp's *RANDOM-STATE* global object, which is initialized to a constant value in many Lisp implementations.
A program might want to repeat a sequence of random numbers to repeat an output, possibly for legal reasons.
If many processes effectively share a random number generator, one process might want to load the generator's seed, use it for a while, & save it to a file before the process exited. That might be necessary for a web site implemented with CGI programs that needed to generate GUIDs.
Gene Michael Stover 2008-04-20