javascriptでGUIDを作成してみる

Widgetをリファクタリングしまくっていて、その過程でjavascriptのライブラリをまとめているんですがとある事情で任意のIDを作成したくなったのでなんとなくGUIDを作成するライブラリをjavascriptで作成してみました。

// BlzGuid.js
//
// Bullseye is freely distributable under the terms of an BSD license.
// Copyright (c) 2006-2007, makoto_kw (makoto.kw@gmail.com) All rights reserved.
if (typeof(Blz)=='undefined') Blz=function() {};
// format: {04088A16-ADBF-4769-9416-5CA48817BC86}
Blz.Guid= Class.create();
Blz.Guid.prototype = {
initialize: function(guid) {
this.strings = new Array("0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F","F");
if (guid) this.guid = guid;
else this.gen();
},
randx: function(length) {
var ret="";
var cnt=0;
while (cnt<length) {
var n = Math.floor(Math.random()*16); // 0-16..
ret += this.strings[n];
cnt++;
}
return ret;
},
gen: function() {
this.guid = "{"+this.randx(8)+"-"+this.randx(4)+"-"+this.randx(4)+"-"+this.randx(4)+"-"+this.randx(12)+"}"
},
toString: function() {
return this.guid;
}
};

シンプルですが適当すぎて効率が悪いです・・・今まとめてるライブラリがprototype.jsを使うこと前提になっているので、prototype.jsのClassオブジェクトを使っています。Classオブジェクトではnewした時点でinitialize関数が呼ばれるようになっており、上記のとおりnewしたときに引数がなければ内部でGUIDを作成します。

つまり下記のように使えます。

var guid = new Blz.Guid();
alert(guid.toString());

16進数一桁ずつを乱数で作成しているのですが、ちょっとダサイことをしています。JavaScriptのMath.random() は0.0から1.0までの疑似乱数です。16*Math.random()とすると16種類の整数ができると思いきや・・・これは0.0から16.0までの値ができることになるので0を含めると17種類の整数ができることになるのです。じゃぁ、15*Math.random()にすれば16種類の整数ができるのようになるのですが、これはこれで問題があって、小数点切り捨てにすると「15」ができるのはMath.random()が1.0になるときしかないので「15」がでる確率だけが減ってしまいます。小数点切り上げにすると今度は「0」がでる確率が減ってしまい、四捨五入にすると若干良い感じになるのですが、「1」がでるケースは値が0.5から1.4999..まであるのに対して「0」がでるケースは0から0.49999…、「15」がでるケースは14.5から15.0までと、「0」と「15」は他の「1」から「14」までの整数と比べて確率が半分になってしまいます。

結局、乱数は16*Math.random()で小数点切り捨てにして、Math.random()が1.0がでたときは特別に「15」に割り当てるということにしています。このためthis.stringsがサイズ17の配列になっています。あんまりよく考えないでこうしたのですが本当はもっと良いやり方がある気がします。。。

ともあれ、一部のソースコードはhttp://trac.makotokw.com/publicで公開しているのですが、たまにはこんな感じで作ったライブラリを紹介していこうと思います。