30万件のデータから「値が 20 〜 25 の値の間のものをとってこい」とかが0ミリ秒とかでとってこれちゃう【ちょっぱやキーバーリューストア】作ったよ!

今回つくったActionScript3用「KeyValueStore」は
30万件のデータからのサーチでも、結果を約0ミリ秒でとってくることができます。

  • オブジェクト(DisplayObjectとかなんでも)をキーにして値を管理
  • 「ある値からある値の間のオブジェクト」とか「ある値のオブジェクト」などの参照系処理が高速にできる
  • 更新系処理もそこそこ高速
  • 一度に1万件くらいの挿入であれば結構一瞬
  • データ件数が多くなってもパフォーマンスがほとんど落ちない

のような特徴があります。

テスト結果

テストしたのは、

データ件数   300000件
   0〜30000のランダムな値

を登録した状態でのテストです。
挿入に関しては30万件挿入だとさすがに、かなり時間がかかってしまうので、
実際に大量にデータ挿入を行う時は、5000〜10000件程度づつ分割して挿入していくと良いと思います。



値が20〜30の間のデータを探すのに
リストをforループで探した場合と今回のクラスを使用した場合で比較しました



結果は・・・・



for loop   230msec
KeyValueStore   0msec

ActionScriptなのでmsec以下の精度で測れていません。マイクロ秒とかとれたっけ?

相当早いです!

ソース

配布ページとかめんどかったのでとりあえず。ソースをそのまま・・・・
そのうちどっかにおきます

/**
 * copyright kaw( http://d.hatena.ne.jp/toytools/ )
 */
package net.toytools.core.data 
{
    import flash.utils.Dictionary;
    
    /**
     * Objectなど何でもキーにして、値を管理します。
     * ある値からある値の間に入っているオブジェクトのリストなどを
     * ちょっぱやで取得できます。
     * @author kaw( toytools ).
     */
    public class KeyValueStore 
    {
        
        //------- CONST ------------------------------------------------------------------------
        //------- MEMBER ------------------------------------------------------------------------
        private var _list:Array;
        private var _objectDict:Dictionary;
        //------- PUBLIC ---------------------------------------------------------------------
        /**
         * 
         */
        public function KeyValueStore() 
        {
            clear();
        }
        
        
        /**
         * 内部データを全て削除.
         */
        public function clear():void {
            _list = new Array();
            _objectDict = new Dictionary();
        }
        
        
        /**
         * データの追加.
         * @param	obj
         * @param	numericValue
         */
        public function add( obj:* , numericValue:Number ):void {
            if ( _objectDict[obj] != undefined ) {
                throw new Error("KeyValueStore.add()  Error::既に存在するキーです.");
            }
            var keyValueStoreItem:KeyValueStoreItem = new KeyValueStoreItem( obj , numericValue );
            _objectDict[obj] = keyValueStoreItem;
            var bsResult:BinarySearchResult = _doCustomBinarySearch( numericValue );
            var insertTarget:int = ( bsResult.isFinded() ) ? bsResult.getMinIndex() : bsResult.getMaxIndex();
            _list.splice( insertTarget , 0 , keyValueStoreItem );
        }
        
        
        /**
         * 値の更新.
         * @param	obj
         * @param	numericValue
         */
        public function update( obj:* , numericValue:Number ):void {
            var keyValueStoreItem:KeyValueStoreItem = _objectDict[obj] as KeyValueStoreItem;
            if ( keyValueStoreItem == null ) {
                throw new Error("KeyValueStore.update()  更新対象のオブジェクトが見つかりません.");
            }
            remove( obj );
            add( obj , numericValue );
        }
        
        
        /**
         * 削除.
         * @param	obj
         */
        public function remove( obj:* ):void {
            var keyValueStoreItem:KeyValueStoreItem = _objectDict[obj] as KeyValueStoreItem;
            delete _objectDict[obj];
            if ( keyValueStoreItem == null ) {
                return;
            }
            var bsResult:BinarySearchResult = _doCustomBinarySearch( keyValueStoreItem.getNumericValue() );
            if ( !bsResult.isFinded() ) {
                return;
            }
            for ( var i:int = bsResult.getMinIndex() ; i <= bsResult.getMaxIndex() ; i++ ) {
                if ( KeyValueStoreItem( _list[i] ).getObj() == obj ) {
                    _list.splice( i , 1 );
                    return;
                }
            }
        }
        
        
        
        
        /**
         * NumericValueが特定の値の間のObjectのリストを取得する.
         * @param	minNumericValue
         * @param	maxNumericValue
         * @return
         */
        public function between( minNumericValue:Number , maxNumericValue:Number ):Array {
            var minBSResult:BinarySearchResult = _doCustomBinarySearch( minNumericValue );
            var maxBSResult:BinarySearchResult = _doCustomBinarySearch( maxNumericValue );
            
            var startIndex:int = minBSResult.isFinded() ? minBSResult.getMinIndex() : minBSResult.getMaxIndex();
            var endIndex:int   = maxBSResult.isFinded() ? maxBSResult.getMaxIndex() : maxBSResult.getMinIndex();
            
            var result:Array = new Array();
            for ( var i:int = startIndex ; i <= endIndex ; i++ ) {
                result.push( KeyValueStoreItem( _list[i] ).getObj() );
            }
            return result;
        }
        
        
        /**
         * ある特定のNumericValueのオブジェクトのリストを取得する.
         * @param	numericValue
         * @return
         */
        public function getObjectList( numericValue:Number ):Array {
            var result:Array = new Array();
            var bsResult:BinarySearchResult = _doCustomBinarySearch( numericValue );
            if ( !bsResult.isFinded() ) {
                return result;
            }
            for ( var i:int = bsResult.getMinIndex() ; i <= bsResult.getMaxIndex() ; i++ ) {
                result.push( KeyValueStoreItem( _list[i] ).getObj() );
            }
            return result;
        }
        
        
        /**
         * オブジェクトのNumericValueを取得する.
         * @param	obj
         */
        public function getNumericValue( obj:* ):Number {
            var keyValueStoreItem:KeyValueStoreItem = _objectDict[obj] as KeyValueStoreItem;
            if ( keyValueStoreItem == null ) {
                throw new Error("KeyValueStore.getNumericValue()  取得対象のオブジェクトが見つかりません.");
            }
            return keyValueStoreItem.getNumericValue();
        }
        
        
        //------- PRIVATE ---------------------------------------------------------------------
        /**
         * 
         * @param	numericValue
         * @return
         */
        private function _doCustomBinarySearch( numericValue:Number ):BinarySearchResult {
            var len:int = _list.length - 1;
            var min:int  = 0;
            var max:int  = len;
            var mid:int = Math.floor( (min + max) / 2 );
            //探索
            while(min <= max && KeyValueStoreItem( _list[mid] ).getNumericValue() != numericValue){
                if( KeyValueStoreItem( _list[mid] ).getNumericValue() > numericValue){
                    max = mid - 1;
                }else{
                    min = mid + 1;
                }
                mid = Math.floor( (min + max) / 2 );
            }
            //結果返却
            if ( min <= max ) {
                min = mid;
                max = mid;
                //最小INDEXを探す.
                while ( 0 < min && KeyValueStoreItem( _list[ min - 1 ] ).getNumericValue() == numericValue ) {
                    min--;
                }
                //最大INDEXを探す.
                while ( max < len && KeyValueStoreItem( _list[ max + 1 ] ).getNumericValue() == numericValue ) {
                    max++;
                }
                return new BinarySearchResult( min , max , true );
            }else {
                return new BinarySearchResult( Math.min( min , max ) , Math.max( min , max ) , false );
                
            }
        }
        
        //------- INTERNAL ---------------------------------------------------------------------
        
    }
    
}

/**
 * 内部バイナリーサーチの結果.
 */
class BinarySearchResult {
    private var _minIndex:int;
    private var _maxIndex:int;
    private var _isFinded:Boolean;
    public function BinarySearchResult( minIndex:int , maxIndex:int , isFinded:Boolean ):void {
        _minIndex = minIndex;
        _maxIndex = maxIndex;
        _isFinded = isFinded;
    }
    
    public function isFinded():Boolean {
        return _isFinded;
    }
    
    public function getMinIndex():int {
        return _minIndex;
    }
    public function getMaxIndex():int {
        return _maxIndex;
    }
    public function toString():String {
        return "min : " + _minIndex + "  max : " + _maxIndex;
    }
}



/**
 * KeyValueStore用に作成された内部保持クラス.
 */
class KeyValueStoreItem{
    private var _obj:*;
    private var _numericValue:Number;
    
    public function KeyValueStoreItem( obj:* , numericValue:Number ):void {
        _obj          = obj;
        _numericValue = numericValue;
    }
    
    public function getNumericValue():Number {
        return _numericValue;
    }
    
    public function getObj():*{
        return _obj;
    }
    
    public function toString():String {
        return "NumericValue :: " + _numericValue + "    " + _obj;
    }
    
}

空中地球コントロールを作ってみました


ToytoolsEarth Air
空中地球コントロールを作ってみました。
↓↓↓YouTubeはこちら↓↓↓


あの映画のインターフェイス的なサムシングです。
作りこみとかしてないので(実装2,3日)、怪しいところとかありますが気にしないでください。




BGM:
地図から消えた街/e-Co
http://wacca.fm/m/11337

今開いているページに近いページやハテブ情報を表示するFireFoxアドオンを作ってみました


FireFoxアドオン「ToytoolsOtemoto」をリリースしました
今見ているページに関連する(ハテブなどの)情報がサイドバーにでてきます

→Toytools Otemoto(beta.)ダウンロードはこちら


ソーシャルブックマークの情報


ソーシャルブックマークの情報表示
現在見ているページの、はてなブックマーク、Yahooブックマーク、LivedoorClipのブックマーク情報が表示されます。
ブックマークの詳細に飛んだり、現在見ているページを各ソーシャルブックマークでブックマークしたりできます。

はてなブックマーク


「Hatena」タブを押して読み込みボタンをクリックすると、
はてなAPIを使って、現在見ているページの「はてなブックマーク」に関する情報を表示できます

  • ブックマーク/コメント一覧
  • 似たサイト

が見れます

デリシャス


「delicious」タブを押して読み込みボタンをクリックすると、
デリシャスAPIを使って、現在見ているページの「デリシャス」に関する情報を表示できます。

  • タグ一覧
  • タグから取得した似たサイト

が見れます

コメント


一応、URLに対してコメントつけたりとかもできます




JS用イベントドリブン実装に必須EventDispatcher作ったよ(this固定機能付)

必要になったので作ってみた。
this固定してdispatchできるよ!



ちゃちゃっとさっき作ったばっかなので、ほとんどテストしてません・・・
多分平気!
とりあえずFireFoxで必要だったのでIEとかでは動かしてもないので動かないかも。




Class.create();の部分だけprototype.js使ってるので、prototype.jsつかってなければ書き換えてください。

コード

//EventDispatcher.js
/**
 * EventDispatcher
 * @author kaw
 */
var EventDispatcher = Class.create();
EventDispatcher.prototype = {
	/**
	 * 
	 * @type Array
	 */
	_targetList:null,
	
	/**
	 * Constructor
	 * @param {} subClassInstance
	 * @usage
	 *         new EventDispatcher( this );
	 */
	initialize:function( subClassInstance ){
		this._targetList = new Array();
		var reference    = this;
		subClassInstance.dispatchEvent       = function( ){
			reference.dispatchEvent.apply( reference , arguments );
		}
		subClassInstance.addEventListener    = function( ){
			reference.addEventListener.apply( reference , arguments );
		}
		subClassInstance.removeEventListener = function( ){
			reference.removeEventListener.apply( reference , arguments );
		}
	},
	
	/**
	 * イベントを発行します.
	 * @param String event
	 * @param {} broadCastObject
	 * @usage
	 *         this.dispatchEvent( "change" , { id:1212 , type:"AAAA" } );
	 */
	dispatchEvent:function( event , broadCastObject ){
		if( this._targetList[event] == undefined ){
			return;
		}
		var list = this._targetList[event];
		var len  = list.length;
		for( var i = 0 ; i < len ; i++ ){
			var o       = list[i];
			var handler = o['handler'];
			var ref     = o['ref'];
			if( ref == undefined ){
				handler( broadCastObject );
			}else{
				handler.apply( ref , [ broadCastObject ] );
			}			
		}
	},
	
	/**
	 * イベントを追加します.
	 * @param String event
	 * @param Function handler
	 * @param {} ref 必須ではありませんが指定すると、コールバック先のthis参照がrefで固定されます.
	 * @usage
	 *         target.addEventListener( "change" , this._onChanged , this );
	 */
	addEventListener:function( event , handler , ref ){
		if( this._targetList[event] == undefined ){
			this._targetList[event] = new Array();
		}
		/*
		var list = this._targetList[event];
		var len  = list.length;
		for( var i = 0 ; i < len ; i++ ){
			var o       = list[i];
			var _handler = o['handler'];
			if( _handler == handler ){
				//登録済み.
				return;
			}
		}
		*/
		this.removeEventListener( event , handler );
		this._targetList[event].push( { handler:handler , ref:ref } );
	},
	
	/**
	 * イベントを削除します.
	 * @param String event
	 * @param Function handler
	 * @usage
	 *          target.removeEventListener( "change" , this._onChanged );
	 */
	removeEventListener:function( event , handler ){
		if( this._targetList[event] == undefined ){
			return;
		}
		var list = this._targetList[event];
		var len  = list.length;
		for( var i = 0 ; i < len ; i++ ){
			var o       = list[i];
			var _handler = o['handler'];
			if( _handler == handler ){
				list.splice( i , 1 );//delete
				return;
			}
		}
	}
}


/**
 * EventDispatcherに対象のオブジェクトを追加します.
 * @param {} obj
 * @usage
 *         EventDispatcher.register( this );
 */
EventDispatcher.register = function( obj ){
	return new EventDispatcher( obj );
}

使い方

var TestEventDispatcher = Class.create();
TestEventDispatcher.prototype = {
	initialize:function(){
		//イベントDispatcherに登録
		EventDispatcher.register( this );
	},
	callTestDummyEvent:function(){
		//イベントのdispatch
		this.dispatchEvent(  "testDummyEvent" , ">>>ここにはオブジェクトとかdispatch先の引数に渡したいものを指定できますよ!" );
	}
}

var Test = Class.create();
Test.prototype = {
	_member:null,
	initialize:function( value ){
		this._member = value;
		
		var testEventDispatcher = new TestEventDispatcher();
		//リスナーの登録
		//第三引数にコールバック先でthisを固定する参照を渡すことができます
		testEventDispatcher.addEventListener( "testDummyEvent" , this._onChanged , this );
		
		//Test!!
		testEventDispatcher.callTestDummyEvent();
	},
	_onChanged:function( event ){
		alert( "値のdispatchできてる?? >> " + event );//値のdispatchできてる?? >> ここにはオブジェクトとかdispatch先の引数に渡したいものを指定できますよ!
		alert( "thisとおってる?? >> " + this._member );//thisとおってる?? >> Hello!
	}
}
new Test( "Hello!" );

怖くなるくらい優秀なCMSらしい「concrete5」を試してみた。日本語つかえたよ。


     



moongift.jp 必見!怖くなるくらい優秀なCMS「concrete5」
らしいです。


フムフム。


なにやらよさそうなので試してみました。
日本語化ける!とかブクマされてたので日本語化対応だけついでにやろうかな〜とか思ったのだけど特に化けることは無かったです。うーむ


PHPMySQLは両方内部エンコードUTF-8で。

ダウンロードして配置する

http://www.concrete5.org/
ここからダウンロードして解答し、適当なWEBサーバー上におきます
※特にDocumentRoot直下の必要ありません

MySQLの設定をチェックする

my.ini(my.conf)に

sql-mode="STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"

の記述がある場合にはコメントアウトしておく
STRICT_TRANS_TABLESの制約に違反したSQLが吐かれるのでこれをコメントアウトしておかないとインストールできません。
※ほんとはソースの方なおしたほうがよいのだろうけどとりあえず

DB作る

MySQLにログインしてDBつくります
※下の例はtestconcreteってDB名にしてます。
※一応DEFAULT CHARACTER SET utf8;を忘れずに。

CREATE DATABASE testconcrete DEFAULT CHARACTER SET utf8;

アクセスしてインストールを完了させる

配置したディレクトリにアクセスしてみます

こんな感じの画面が表示されるので
項目を全部埋めてインストールを完了させます

インストール完了


こんな感じのページが出ます

日本語テスト

新しいページの作成、ページタイトルの日本語テスト、内容の日本語テストなどひととおりざっくり試しましたが特に文字化けとかしませんでした

↑こんな感じ

感想

たしかに。すごい。
イメージ的にはWEB上で作ってけるDreamWeaverとかそんなイメージ。
なので多少WEBのこと知らないと触りにくいのかも。


定型更新でない、自由にページを増やして行きたいようなサイトだったら相当いいのではないでしょうか。


操作感は相当よさげです。
用途に合わせてWordPress(とかMT)やWiki系などと使い分けていくのがベストな感じだとおもいます。

Flash開発(動作チェック)に必須FireFoxアドオン

Flash Switcherアドオンをインストールする

http://www.sephiroth.it/firefox/flash_switcher/index.php
↑このページのInstall now!から


ブラウザの右下にこんな(f)アイコンが出てたらインストール成功です。


切り替えられるバージョンを追加する

必要なバージョンのFlashPlayerをダウンロードする

インストールしたままではver7,8,9の切り替えしかできないので
ver9(デバッグ版)とver10を追加します
http://www.adobe.com/jp/support/kb/ts/228/ts_228683_ja-jp.html
(リンク切れなので変更)
上記ページから、必要なバージョンのFlashPlayerをダウンロードします。

追加するバージョンをインストールする

ダウンロードしたzipを解凍すると
それぞれのマイナーバージョンのフォルダの中に全てのプラットフォームのアーカイブが入っています
バージョンの後ろにつく名前によってプラットフォームが変わります。

FireFox用が必要なので「_win」となっているものを選んでインストールします
(例:flashplayer9r124_win_debug.exe)

インストールしたFlashPlayerを保存する

ブラウザの右下の(F)アイコンからSaveAsを選択

適当な名前をつける

上記作業を繰り返す

必要なバージョンを

  1. インストール
  2. 登録

という作業を繰り返します

完了


上の図は9のデバッグ版と10のデバッグ版を追加した状態
1クリックでFlashPlayerのバージョンを切り替えて動作確認ができます