なぜamachangが「すごい」といっているかおわかりになるだろうか。
IT戦記 - ActionScript 3.0 の勉強会資料 経由 amachang - ActionScript 3.0 勉強会資料flash.utils.Dictionary
このクラスはすごい
ある意味 ECMAScript の
常識をぶち壊す
神秘のようなもの
ECMAScript もビビる!
俺もビビる
あ、ちょ、痛っ
石投げないで><
以下のJavaScriptを考えてみよう。
var a1 = []; var a2 = [1,2,3] var o1 = {}; var o2 = {'one':1, 'two':2, 'three':3}; var dict = {}; dict[a1] = 'a1'; dict[a2] = 'a2'; dict[o1] = 'o1'; dict[o2] = 'o2'; var result = ''; for (p in dict){ result += '(' + p + ' = ' + dict[p] + ') '; }
実行結果は以下のとおりとなる。
オブジェクトのキーとしてオブジェクトを利用する場合、JavaScriptではオブジェクトそのものではなく、object.toString()
の値がキーになってしまうので、こういう結果になる。
このことは、Perl 5でも同様なのだが、少しはましに見える。
#!/usr/local/bin/perl use strict; use warnings; use CGI; my $a0 = []; my $a1 = [1,2,3]; my $h0 = {}; my $h1 = {one=>1,two=>2,three=>3}; my $q = CGI->new(); my %hash; $hash{$a0} = 'a0'; $hash{$a1} = 'a1'; $hash{$h0} = 'h0'; $hash{$h1} = 'h1'; $hash{$q} = 'q'; print "$_ => $hash{$_}\n" for keys (%hash);
実行結果:
ARRAY(0x180127c) => a1 ARRAY(0x1801180) => a0 HASH(0x182c4d8) => h0 HASH(0x182c460) => h1 CGI=HASH(0x182c430) => q
このように、同じオブジェクトの文字列化でも、Perlの場合IDに相当する情報が取れるので、そのままでもある程度使える。
しかし、このようにすると馬脚ならぬ駝脚が現れる。
#最後のprintを以下のとおり変更 print "ref($_) = ", ref $_, "\n" for keys (%hash);
実行結果:
ref(ARRAY(0x180127c)) = ref(ARRAY(0x1801180)) = ref(HASH(0x182c4d8)) = ref(HASH(0x182c460)) = ref(CGI=HASH(0x182c430)) =
このとおり、ハッシュキーはあくまでもオブジェクトを文字列化したものであって、オブジェクトそのものではない。
ところが、Tie::RefHash
を使うだけで、この壁は簡単に乗り越えられる。
use Tie::RefHash; # ... tie my %hash, 'Tie::RefHash'; # ...
実行結果:
ARRAY(0x180127c) => a1 ARRAY(0x1801180) => a0 HASH(0x1835bb0) => h1 HASH(0x1835c28) => h0 CGI=HASH(0x1835b80) => q ref(ARRAY(0x180127c)) = ARRAY ref(ARRAY(0x1801180)) = ARRAY ref(HASH(0x1835bb0)) = HASH ref(HASH(0x1835c28)) = HASH ref(CGI=HASH(0x1835b80)) = CGI
残念ながら、Pure JavaScript でこれと同様のことをやるのは難しそうだ。というのも、JavaScriptでは同じobjectかどうかを確認する===
演算子はあっても、ObjectのIDを取得する方法が現時点では見当たらないからだ。無理矢理こさえると、こんなところになるだろうか。
function RefHash(){ this.keys = []; this.values = []; this.fetch = function(key){ for (var i = 0, l = this.keys.length; i < l; i++){ if (this.keys[i] === key) return this.values[i]; } return null; }; this.store = function(key, value){ var i; var l = this.keys.length; for (i = 0; i < l; i++){ if (this.keys[i] === key) break; } this.keys[i] = key; this.values[i] = value; return this.keys.length - 1; } } var a1 = []; var a2 = [1,2,3] var o1 = {}; var o2 = {'one':1, 'two':2, 'three':3}; var dict = new RefHash(); dict.store(a1, 'a1'); dict.store(a2, 'a2'); dict.store(o1, 'o1'); dict.store(o2, 'o2'); dict.store(a2, 'A2'); // 上書きのテスト var result = ''; for (var i = 0, l = dict.keys.length; i < l; i++){ var p = dict.keys[i]; result += '(' + p + ' = ' + dict.fetch(p) + ') '; }
実行結果:
見ての通り、リニアサーチなので遅いこときわまりない。Perlの文字列化とか数値化(文字列化した場合の括弧の中身、すなわちアドレス)や、Rubyのobject.object_id
のようなオブジェクトIDはJavaScriptの場合取れないのだろうか....
Dan the RefHasher
このブログにコメントするにはログインが必要です。
さんログアウト
この記事には許可ユーザしかコメントができません。