Xah Lee, 2011-11-07, 2011-11-18
This week's exercise is to write a function insert-random-uuid. When called, it should insert a UUID. Here's some examples of UUID:
0a1cd3bc-96fa-71d1-4338-27092ca4cfa5 1e27a053-60a4-af61-f38d-9f1f123740d6 115024d2-7c74-326e-c9ec-064f42d08b31 070f1f0b-2454-3ffa-4aa2-d6e0652d03fe
Basically, it's a random string of symbols 0 to 9 and a to f, arranged in 8-4-4-4-12 blocks. There are many ways to implement this. I'll post a solution in a couple of days.
If you really don't know elisp, this weekend is a good time to learn as any. This article might get you started: Emacs Lisp Basics.
There are many ways to code this. Here's one:
(random t) (defun insert-random-uuid () "Insert a random universally unique identifier (UUID). UUID is a 32 digits hexadecimal formatted in certain way with dash. Example of a UUID: 1df63142-a513-c850-31a3-535fc3520c3d ." (interactive) (insert (format "%04x%04x-%04x-%04x-%04x-%06x%06x" (random (expt 16 4)) (random (expt 16 4)) (random (expt 16 4)) (random (expt 16 4)) (random (expt 16 4)) (random (expt 16 6)) (random (expt 16 6)) ) ) )
Elisp's random function can be called in 3 ways:
(random t). Set a random seed based on current time and PID (process ID).(random n). Returns a random number between 0 and n-1, including possible 0 and n-1.(random). Returns a random number between 0 and 2^29-1, inclusive.Elisp's documentation does not say exactly how large a pool the random number is generated from, only that “On most systems, this is 29 bits' worth.”. (See: (info "(elisp) Random Numbers"))
UUID is basically a random number between 0 and 2^128-1, inclusive. Since emacs's random is only from a space of 2^29 possibilities, so we need to call it several times, to make sure that we don't generate from a small space and expand it to larger space.
Note: if you don't know already, the random number generating function in almost all computer languages are called Pseudorandom number generator. They are numbers generated in a predictive way. For example, if you start emacs, then if you call (random 100) few times, you will always get this sequence: {0 38 17 70 …}.
(For this to work, make sure none of your init files calls (random t). (start emacs by emacs -Q to prevent loading any init files).)
It's a good idea to set a seed when you call random in elisp, and the setting of seed should be outside of your function.
The meaning of randomness is a deep math topic. In general, there is no one absolute universal definition. You might check relevant articles on Wikipedia, starting with Random sequence.
Note, according to Wikipedia's description of UUID, in the “Variants and Versions” section, some digits should be fixed when the UUID is generated by a random or pseudo-random number. That is, in the format:
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
The “M” should be “4” and the N should be one of {8, 9, A, B}.
Also, if you really want a robust UUID implementation, best is to make a shell call to the unix shell's “uuidgen”, like this:
(defun insert-random-uuid () (interactive) (shell-command "uuidgen" t))
Big thanks to yurivkhan and Jon Snader for discussion about UUID. See their comments at bottom of: http://xahlee.blogspot.com/2011/11/emacs-lisp-exercise-insert-random-uuid.html.
2011-11-18 Addendum. Here's another implementation, by Christopher Wellons. It's excellent in that it uses several random factor from emacs, then call md5 to generate the UUID.
;; by Christopher Wellons. 2011-11-18. Editted by Xah Lee. (defun insert-random-uuid-2 () "Insert a UUID. This uses a simple hashing of variable data." (interactive) (let ((myStr (md5 (format "%s%s%s%s%s%s%s%s%s%s" (user-uid) (emacs-pid) (system-name) (user-full-name) (current-time) (emacs-uptime) (garbage-collect) (buffer-string) (random) (recent-keys))))) (insert (format "%s-%s-4%s-a%s-%s" (substring myStr 0 8) (substring myStr 8 12) (substring myStr 13 16) (substring myStr 17 20) (substring myStr 20 32)))))