企業が好む表現対策 Part1<占い>::Circuit Maker(JP)
※ワードとはここでは画面に表示する文字列を表す。
必要な要素をまとめる
まず、絶対に必要とされる要素として、「一ヶ月単位でワードを繰り返したい」「毎日違うテキストを表示したい」と、「ワードは日数分は用意出来ない」ということはよくあるだろう。一ヶ月単位でワードを繰り返すということは、一ヶ月30日の時に20種類のワードが用意されていた場合、20日間は重なることなく20種類出現し、残りの10日間は再び20種類の中から10種類が現れる。どういう機能を載せて実現していくか並べてみると、
・XMLから一定数のワードリストを読み込む(修正のしやすさを考慮)
・毎日違うテキストを表示する(基本オーダーより)
・同じ日は同じテキストを表示する(基本オーダーより)
・月ごとにリストを再生成する(応用として、余裕があれば)
動作の概要
まず、xmlから読み込まれたドキュメントからワードの配列を生成する。次に、31日分の配列を作成、その中にワードをループで順に挿入する。ワードを入れた配列をシャッフルして順の一定さを無くす。今日の日付をindexとして配列からワードを取り出す。一回目だけ作成されたリストがローカル共有オブジェクトに保存されて、2回目以降の訪問では保存されたリストからワードが取り出されるので、毎回ランダムにはならなくなる。月ごとに違うリストを持たせることもできる。

実際に動作しているサンプルは記事の最後にある。このサンプルではXMLから取り出した2つの文字を使って表示する。このsourceも自由に利用出来るようにしてある。
var cm:CircuitMaker; var cl:CircuitXML = new CircuitXML(); function onXMLLoad(){ cm = new CircuitMaker(cl.getList(), "uranai", false); result.text = (cm.getString()); } cl.addEventListener("onXMLLoad", this); cl.load("norm.xml");
XMLデータの構造
これが読み込まれるXMLでとても単純な構造でワードリストが定義されている。wordタグが実際表示されるワードを持つ。
<?xml version="1.0" encoding="UTF-8" ?> <circuit> <word id="1">日は夕焼け</word> <word id="2">日は夕立</word> </circuit>norm.xml
XMLローダ
CircuitXMLは中にXMLを持ち、上で定義したXMLドキュメントを読み込み、CircuitMakerで利用できるデータ(配列)に変換するところまでの役割を持つ。
public function load(url):Void{ var xml:XML = new XML(); var owner:CircuitXML = this; xml.ignoreWhite = true; xml.onLoad = function(success:Boolean):Void{ if(success){ owner._o.list = owner._xmlToArray(this); owner.dispatchEvent({type:"onXMLLoad"}); }else{ trace("Loader error."); } }; xml.load(url); }xmlから配列を生成する。これにはこのXMLの構造がnorm.xmlのような形式であることが分かっていなければならない。
private function _xmlToArray(xml:XML):Array{ var strList:Array = xml.firstChild.childNodes; var i:Number; var result:Array = new Array(); for(i = 0; i < strList.length; i++){ result.push(strList[i].firstChild.nodeValue);//<word>nodeValue</word> } return result; }
本体の作成
テキストを日付毎に巡回して表示するために最も重要なCircuitMakerクラスではXMLローダにより作成された配列データを基にしてまずローカル共有オブジェクトにあるかを確認する。すでに作成されていた場合そのリスを使うが、無い場合31日分のワードリストを作成し、ローカルに保存する。ここでString型に一度変換してデータサイズを小さくする工夫をしている。updateEveryMonthフラグがtrueの場合、このリスト生成を毎月必ず行う。
public function CircuitMaker(strList:Array, soName:String, updateEveryMonth:Boolean) { var now:Date = new Date(); var so:SharedObject = SharedObject.getLocal(soName, "/"); if(so.data.list == undefined){ so.data.list = _createList(strList, CIRCUIT_SIZE).join(DELEMITER);//String変換して保存 so.data.month = now.getMonth();//リストを作成した月も保存 }else{ if(updateEveryMonth && so.data.month != now.getMonth()){//月が変わった時も作成 so.data.list = _createList(strList, CIRCUIT_SIZE).join(DELEMITER); so.data.month = now.getMonth(); } } _list = so.data.list.split(DELEMITER); }31日分の配列にワードリストからワードが割り当てられ、乱数でindex間を入れ替えることで配列の順番をバラバラに組み直す。ワードリストの数単位でシャッフルすることが重要である。31個まとめてシャッフルをするとワードが連続する可能性があるためである。
private function _createList(arr:Array, size:Number):Array{ var i, count:Number = 0; var len:Number = arr.length; var result:Array = new Array(); while(1){ var unit:Array = new Array(); for(i = 0; i < len; i++){//ワードリストを取り出す unit[i] = arr[i]; if(++count == size){//31になったらやめる result.push(_shuffleList(unit)); while(result.length > 1){ result[0] = result[0].concat(result.pop());//配列同士の連結 } return result[0]; } } result.push(_shuffleList(unit)); } } private function _shuffleList(arr:Array):Array{ var i:Number = 0; var len:Number = arr.length; for(i = 0; i < len; i++){ var tmp:Object = arr[i]; var randomNum:Number = random(len); arr[i] = arr[randomNum]; arr[randomNum] = tmp; } return arr; }