スポンサーサイト
投稿日時 : -------- --:--
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
-------- --:-- | スポンサー広告
フィボナッチ数列でJavascriptアルゴリズム速度比較とAnarchy Golf
投稿日時 : 2010-03-02 05:56
更新日時 : 2010-03-08 00:35
 プログラマーの力量を見極める--面接官になったら尋ねるべき質問実例集 - IT業界を生き抜く秘密10箇条 - ZDNet Japan
 
↑の記事を読んでて文系出なのを言い訳にして今までフィボナッチ数列が何なのかよく知らないまま来てしまったことに危機感を覚えたのでやってみた。基本的な話はグーグル先生にまかせておくとして、実際のコードを紹介。
 
 巷にあふれるフィボナッチ数列生成コードはざっと見た感じほとんど再帰でやってて、どうもそれが納得いかなかった。最適化して計算量が少ないとしても実行コストからして関数呼び出しは避けるべきかと。なので再帰処理はせず for や while だけでがんばることに。以下いくつかのコードで処理速度の比較をやって解説なんか加えたりしてますが、まあまだフィボナッチ数列を知ってから24時間経ってないので生暖かく読んでください。ベンチマークは JScript 5.7 と SpiderMonkey 1.8。
 

テスト内容

 フィボナッチ数列を 1 から Infinity(Javascriptで表現できる数値の上限)に達するまで生成して Infinity になったらリセットしまた最初から生成していく、というのを1000ループする処理をSpiderMonkey 1.8 と JScript 5.7 を使って各アルゴリズムの処理速度を検証していく。アルゴリズム比較というよりも Javascript の特性の話寄りかも。

テスト環境

  • Intel Core 2 Quad Q9550 2.83GHz
  • 4GB / 3.25GB認識、残りRAMディスク割り当て
  • Windows XP Pro SP3
  • SpiderMonkey 1.8
  • JScript 5.7
 
以下のコードで処理中の print がコメントアウトされているのは SpiderMonkey の標準出力がボトルネックすぎてフィボナッチ数列の生成速度自体がわからなくなるので。コメント外せば普通に数列が表示されます。
 

一般的な再帰処理

 まずは一般的な再帰処理から。404 Blog Not Found:アルゴリズム百選 - フィボナッチ数列にO()を学ぶの一番最後で紹介されている、最も計算量の少ないとされるコードを借りてきて、今回のテストに合わせてちょっと改変。ループ回数を 1476 と決め打ちしてるのはよろしくないがこれで200msほど稼げる。
   
一般的な再帰処理
//var print = function(x){WScript.Echo(x)} //JScript用
var s = new Date(); //処理開始時刻保存
function fib(n){
  if (fibs[n]) return fibs[n];
  if (n <= 2) return 1;
  return fibs[n] = fib(n-1) + fib(n-2);
}
var fibs;
var n;
for(var i=0; i<1000; i++){ //1000回ループ
	fibs = {};
	for (n = 1; n <= 1476; n++) fib(n); //print(fib(n));
}
print(new Date()-s); //処理にかかった時間をミリ秒で表示
SpiderMoneky 1.8JScript 5.7
17849ms9890ms
27843ms9890ms
37824ms9891ms
47867ms9859ms
57867ms9860ms
遅い!遅すぎる。すごい待たされる感があります。
 

速度重視版

 次に最速に近いスコアをマークしたやつを(最速のコードは驚きと共に最後に登場)。
 
配列要素直接指定でwhileループ : 速度重視版
//var print = function(x){WScript.Echo(x)} //JScript用
var s = new Date(); //処理開始時刻保存
for(var i=0, f, j, a; i<1000; i++){ //1000回ループ
	f = 1, j = 0, a = [0, 1];
	while(j < 1476){ //1476ループ目でfがInfinityに達する
		//print(f);
		a[j + 2] = (f += a[j++]);
	}
}
print(new Date()-s); //処理にかかった時間をミリ秒で表示
SpiderMoneky 1.8JScript 5.7
1856ms2078ms
2857ms2078ms
3856ms2078ms
4851ms2062ms
5853ms2078ms
 一般的な再帰処理に比べて1/10近いスコア。a への代入を push などを使うのではなく a[j + 2] のように要素を直接指定している。
 

速度重視版を少し読みやすくするとどうなるか

 速度重視版のコードは一般的には複数行に分けて書くようなものを1行にまとめている。以下のようなコードは読みにくい上に処理される順序がわかりにくいので悪いコードだとされている。
a[j + 2] = (f += a[j++]);
これを少し読みやすく複数行に分けて書いて速度を測ってみる。
 
配列要素直接指定でwhileループ : 速度重視版 を複数行化
//var print = function(x){WScript.Echo(x)} //JScript用
var s = new Date(); //処理開始時刻保存
for(var i=0, f, j, a; i<1000; i++){
	f = 1, j = 0, a = [0, 1];
	while(j < 1476){ //1476ループ目でfがInfinityに達する
		//print(f);
		f += a[j];
		j++;
		a[j+1] = f;
	}
}
print(new Date()-s); //処理にかかった時間をミリ秒で表示
SpiderMoneky 1.8JScript 5.7
1985ms2281ms
2978ms2291ms
3978ms2297ms
4984ms2297ms
5980ms2313ms
なんとSpiderMonkeyで100msちょっと、JScriptで200ms遅くなってしまった。速度が求められる処理では多少読みにくくても1行にまとめた方が良さそうなことがわかる。処理される順番などについて念入りにたくさんコメントをつけておけばバグを生む可能性もまあ少なくなると思う。
 

配列をpushやshiftで操作

 速度重視版の配列をpushやshiftで操作した場合。ロケット鉛筆的な動作。配列要素は常に2つ。
 
配列をpushやshiftで操作
//var print = function(x){WScript.Echo(x)} //JScript用
var s = new Date(); //処理開始時刻保存
for(var i=0, n, f, a; i<1000; i++){
	for(n=0, f=1, a=[0,1]; n<1476; n++){ //1476ループ目でfがInfinityに達する
		//print(f);
		a.push(f += a.shift());
	}
}
print(new Date()-s); //処理にかかった時間をミリ秒で表示
SpiderMoneky 1.8JScript 5.7
12012ms5516ms
22017ms5500ms
32015ms5500ms
42021ms5500ms
52013ms5515ms
それなりに時間はかかるがまあ許せる範囲。速度重視版との違いは配列操作にpushやshiftを使っている点。pushやshiftは遅い。
 

速度重視版のループ数決め打ちをやめてみた

 やっぱり決め打ちはよくないよ、ということでちゃんとしたらどうなるかテスト。
 
速度重視版のループ数決め打ち無し
//var print = function(x){WScript.Echo(x)} //JScript用
var s = new Date(); //処理開始時刻保存
for(var i=0, f, j, a; i<1000; i++){
	f = 1, j = 0, a = [0, 1];
	while(f < Infinity){ //Infinityになったら終了
		//print(f);
		a[j + 2] = (f += a[j++]);
	}
}
print(new Date()-s); //処理にかかった時間をミリ秒で表示
SpiderMoneky 1.8JScript 5.7
11056ms2157ms
21054ms2141ms
31056ms2172ms
41055ms2156ms
51054ms2156ms
というように200msほど遅くなってしまうわけです。
 

Anarchy Golfに挑戦してみた

 フィボナッチ数列を調べていたら、与えられた問題をいかに少ない文字数のプログラムで実現するかというのを競う anarchy golf(通称あなごる?)というものを発見。ここにフィボナッチ数列 f(1) ~ f(46) までをJavascriptで出力する問題があった。
 
 
で、やってみたわけですが他の人がすごい。1位は32bytesです。32bytesでフィボナッチ!すごすぎです。一応投稿したコードを載せておきます。今のとこ44byteです。
 
anarchy golf - Fibonacci Numbers : 44bytes
for(m=n=o=0,f=1;46>n++;print(o=f),f+=m,m=o);
こっからどう削ればいいものか・・・。たぶんビット演算とかやらなきゃだめっぽいような・・・?←違う気もしてきた。
 
追記:41bytesまできた。
anarchy golf - Fibonacci Numbers : 41bytes
for(f=m=1,n=46;n--;[m,f]=[f+m,m])print(f)
追記:39bytesまできた。
 
追記(2010-03-07 22:21) : 37bytesまできた。
 
追記(2010-03-08 00:35) : 34bytesまできた。ゴールは近いか?
 

Anarchy Golf用コードもベンチマーク

 実は大きな発見がありました。
 
Anarchy Golf用コード : テスト用に一部改変
//var print = function(x){WScript.Echo(x)} //JScript用
var s = new Date(); //処理開始時刻保存
for(var i=0; i<1000; i++){
	for(m=n=o=0,f=1;1476>n++;o=f,f+=m,m=o);
	//for(m=n=o=0,f=1;1476>n++;print(o=f),f+=m,m=o); //print付
}
print(new Date()-s); //処理にかかった時間をミリ秒で表示
SpiderMoneky 1.8JScript 5.7
13144ms1453ms
23146ms1453ms
33152ms1454ms
43147ms1454ms
53149ms1453ms
SpiderMonkeyよりJScriptが速い!?何かの間違いかと思ってコード見返したけど問題ない。ここで偶然にもSpiderMonkeyの特性を発見することになった。
 

SpiderMonkeyでvar宣言は想像以上に重要 - 最速コードへ

 上記テスト結果が衝撃だったので少しコードをいじってみるとSpiderMonkeyで劇的に速くなりました。なんとループ中で使う変数のvar宣言がないことが原因でした。
 今回のコードは文字数を削るために変数 m, n, o, f をvar宣言なしでいきなりグローバル変数として初期化していました。これを前もってforの初期化コードでvar宣言するように変更しただけです。
 
Anarchy Golf用コード : テスト用に一部改変 - var宣言は重要
//var print = function(x){WScript.Echo(x)} //JScript用
var s = new Date(); //処理開始時刻保存
for(var i=0,m,n,o,f; i<1000; i++){
	for(m=n=o=0,f=1;1476>n++;o=f,f+=m,m=o);
	//for(m=n=o=0,f=1;1476>n++;print(o=f),f+=m,m=o); //print付
}
print(new Date()-s); //処理にかかった時間をミリ秒で表示
SpiderMoneky 1.8JScript 5.7
1694ms1453ms
2694ms1437ms
3697ms1453ms
4695ms1438ms
5696ms1453ms
なんと!var宣言するだけでSpiderMonkeyで最速スコアに。JScriptの方は改良前と誤差の範囲。SpiderMonkeyの場合var宣言はスコープの範囲だけの問題ではなく、速度に大きく影響するようです。この原因や仕組みについてはまだ詳しく調べていませんが、スコープチェーンをたどるかたどらないかの違いかもなーとぼんやり考えています。まあいずれにせよ大きな発見でした。
 
かなり速くなったけどまだまだ上はあると思います。anarchy golfもまだまだです。修行あるのみ。
 
 
スポンサーサイト
2010-03-02 05:56 | Javascript | Comment(0) | Trackback(0)
Firefoxの右クリックメニューからSleipnirで開く
投稿日時 : 2008-09-21 23:11
 このあたりを参考にuserChrome.jsを導入。この解説の中で右クリックメニューから現在開いているページをIEで開く「LaunchIE」が紹介されていたが、同じTridentエンジンならSleipnirで開きたいのでLaunchIE.uc.jsをちょこっと改造してLaunchPnir.uc.jsを作ってみた。userChrome.jsとSub-Script/XUL Loaderなどのサブスクリプトローダの導入が完了していることが前提です。
 

■LaunchPnir.uc.jsのダウンロード

 

■sleipnir.exeのパスの設定

 ダウンロードしたらsleipnir.exeのパスをソース内5行目の「Sleipnir_Path: ""」のところに記述してください。円マーク(バックスラッシュ)はエスケープすることに注意。
/* :::::::: LaunchPnir (cf. IE View) ::::::::::::::: */

var LaunchPnir = {
	//input the path of sleipnir.exe
	Sleipnir_Path: "",
(省略)
たとえばこんな感じになります。
/* :::::::: LaunchPnir (cf. IE View) ::::::::::::::: */

var LaunchPnir = {
	//input the path of sleipnir.exe
	Sleipnir_Path: "C:\\Program Files\\Sleipnir\\bin\\Sleipnir.exe",
(省略)
 同じような要領で他のプログラムのパスを指定すればSleipnir以外のものでもURLを渡して起動することができます。
 

■コンテキストメニューの表示名に日本語を使う

 デフォルトではコンテキストメニュー(右クリックメニュー)には「LaunchPnir」と表示されますが、これを「Sleipnirで開く」と日本語で表示したい場合は以下の箇所を
(省略)
	init: function()
	{
		this.mItem = document.createElement("menuitem");
		this.mItem.setAttribute("label", "LaunchPnir");
(省略)
こんな感じにします。
(省略)
	init: function()
	{
		this.mItem = document.createElement("menuitem");
		this.mItem.setAttribute("label", "Sleipnir\u3067\u958B\u304F");
(省略)
日本語をそのまま書くと文字化けするのでUnicodeエスケープしたものを記述します。  

■chromeフォルダに移動

 編集が終わったらchromeフォルダに移動させてください。chromeフォルダのパスは環境によって異なりますがだいたい以下のような感じじゃないかと。
C:\Documents and Settings\[ユーザ名]\Application Data\Mozilla\Firefox\Profiles\[ランダムな英数字].default\chrome\LaunchPnir.uc.js
 

■右クリックメニューからLaunchPnirを選択

 Firefoxを再起動させて適当なページを開いたら右クリックメニューから「LaunchPnir」を選択して下さい。
LaunchPnri demo
 
現在Firefoxで開いているページがSleipnirで開いたハズです。
 
メニューを日本語化した場合は以下のような感じになっているかと。
 LaunchPnir Japanese menu
 
 
2008-09-21 23:11 | Javascript | Comment(8) | Trackback(0)
Javascriptの各メソッドによる行数の数え方と処理速度比較
投稿日時 : 2008-05-25 18:43
行数の数え方: Days on the Moon
 
HTMLのtextareaのvalueやテキストファイルの行数を数えるには改行コード「\n」の数を数えばいいわけですが、どうやって数えるかによって処理速度に大きな差が出るという話です。実験環境はSpiderMonkeyJScript
 
Days on the Moon
行数カウントの実行時間 (10000 回、単位はミリ秒)
改行を含む文字列改行を含まない文字列
SpiderMonkey1.8JScript5.7SpiderMonkey1.8JScript5.7
charAt46816563891360
Array4631047382859
split1081727863
match5022037378
match
(番兵)
543250185110
indexOf952501331
replace446948262
 
 
以下は実験で使用されているコードの説明。
 

■charAt

Days on the Moon
// charAt
var lines = 1;
for (var i = 0, n = s.length; i < n; i++)
if (s.charAt(i) == "\n")
lines++;
charAtで1文字ずつ取り出して\nかどうか調べている。JScriptがなぜこんなに遅いのか分からないがとりあえず避けるべき方法と言える。
 

■Array

Days on the Moon
// Array
var lines = 1;
var chars = s.split("");
for (var i = 0, n = chars.length; i < n; i++)
if (chars[i] == "\n")
lines++;
var chars = s.split("");で1文字ずつ配列に格納する。charAtの時と違ってchars[i]で各配列の値が\nかどうかを調べる。見た目的にはcharAtと大して変わらないがJScriptではこれだけでもそれなりに速くなる。
 

■split

Days on the Moon
// split
var lines = s.split("\n").length;
\nでsplitして得られたArrayオブジェクトのlengthを調べる。速度的にも速いし非常に読みやすく理解しやすい。
 

■match

Days on the Moon
// match
var lines = (s.match(/\n/g) || []).length + 1;
matchを使って\nをグローバル検索し得られたArrayオブジェクトのlengthを調べる。「|| []」があるのは\nが見つからなかった場合に返されるオブジェクトがArrayオブジェクトではなく、lengthプロパティを参照しようとするとエラーになってしまうのでそれを回避するため。ちなみに「[].length」は0が返される。
 
 

■match(番兵)

Days on the Moon
// match (番兵)
var lines = (s + "\n").match(/\n/g).length;
さっきのmatchに番兵を置いたバージョン。「|| []」とする代わりに「\n」を文字列sの末尾に追加し、確実に一つはArrayオブジェクトが生成されるようにし、lengthプロパティ参照でエラーが起こりえないようにしている。
 

■indexOf

Days on the Moon
// indexOf
var lines = 1;
var i = -1;
while ((i = s.indexOf("\n", i + 1)) >= 0)
lines++;
indexOfで\nを検索、見つからなかった場合に返される-1になるまでwhileでループする。意外にもほぼ最速。しかし後述するが検索対象の文字列の内容によって速度が大きく変化するのでいつでも最速というわけにはいかないようです。
 

■replace

Days on the Moon
// replace
var lines = s.length - s.replace(/\n/g, "").length + 1;
sの全体のlengthから\nを削除した文字列のlengthを引くことで\nの数を判定。
 
 

■もう少し詳細に

この行数の数え方: Days on the Moonの記事を受けてトラックバックに
 
文字列処理の性能に関する考察 - blanket-log
 
というのがあってより詳しく調べてくれています。

■charAtとArrayは本当はArrayが断然速い

上記の実験ではArrayとcharAtがどちらも遅く、大して変わらないという印象でしたが、ArrayバージョンではsplitによるArrayオブジェクトへの変換に時間がかかっているだけで、ループ内の処理はcharAtと比べて断然速いようです。
 

■indexOfは検索対象文字数とループ数、時間が比例する

最速かと思われたindexOfですが、文字列中に含まれる検索対象文字(\n)が多くなると一気に速度が落ちるようです。これは改行数に比例してループ数が多くなるため時間もそれに応じて多くかかってしまうから。しかしループ数が少ない場合は新たなオブジェクトの生成などが発生しないぶんだけ非常に高速になる。
 

■splitとmatchは似ている

splitはもとの文字列への参照を持つArrayオブジェクトが生成されるだけで、内部で新たな文字列にコピーが行われるといったことがないので高速になるということらしい。matchも生成されるのはArrayオブジェクトなので、内部処理はsplitと同じようものだろうということらしいです。
 

■実はreplaceが最速

改行数が多い場合はreplaceが最速という結果になるようです。走査処理はsplitと似ているが、最終的に生成されるのが文字列というのがspilitとの違いで、そのArrayオブジェクトとStringオブジェクトの生成コストの差が性能の差になっているようです。
 
 

■まとめ

各方法にはそれぞれ得意とする分野があるので、対象文字列の内容がどのようなものかによって使い分けるのがいいと思われます。charAtは論外ですが。予想できない場合は無難にsplitを使ってれば安心かも。splitが一番可読性が高い気がするし。  
 
2008-05-25 18:43 | Javascript | Comment(0) | Trackback(0)
JavascriptのString.replaceに無名関数を渡して複雑な置換
投稿日時 : 2008-03-03 02:33
 JavascriptでString.replaceに無名関数を渡して、後方参照や変数などを絡めて複雑な置換処理ができる。

■サンプル

 以下のサンプルは文字列「a=%a : b=%b : c=%c」の%付きアルファベットをキーとして連想配列namedから値を取得し、置換するものです。実行結果は「a=0 : b=1 : c=2」となります。
var named = {a:0,b:1,c:2}; //連想配列(Object)
var str = 'a=%a : b=%b : c=%c'; //置換対象文字列
var reg = /%([a-c])/g; //正規表現オブジェクト生成
var result = str.replace(reg,
    function(whole,p1){ //無名関数定義
        return named[p1]; //連想配列から値を取り出す
    }
);
alert(result); //「a=0 : b=1 : c=2」と表示
replaceの中にごちゃごちゃ書きたくない場合は以下のように無名関数を格納した変数を渡してもOK。ただしこのコードを関数内に書くのであればrepはローカル変数になるので問題ないが、関数外に書く際はrepがグローバル変数である必要がないなら避けるべき。
無名関数への参照を格納した変数を渡す例
var named = {a:0,b:1,c:2}; //連想配列
var str = 'a=%a : b=%b : c=%c'; //置換対象文字列
var reg = /%([a-c])/g; //正規表現オブジェクト生成
var rep = function(whole,p1){ //無名関数の参照を変数repに格納
    return named[p1]; //連想配列から値を取り出す
} 
var result = str.replace(reg, rep); //変数repに格納した無名関数への参照を渡す
alert(result); //「a=0 : b=1 : c=2」と表示
後で同じ置換処理をしたいなら、無名関数ではなく名前付き関数を定義しておいてその参照を渡しても構わない。1回しか使わないなら無名関数を使うべき。
名前付き関数への参照を渡す例
var named = {a:0,b:1,c:2}; //連想配列
var str = 'a=%a : b=%b : c=%c'; //置換対象文字列
var reg = /%([a-c])/g; //正規表現オブジェクト生成
function rep(whole,p1){ //名前付き関数をあらかじめ定義
    return named[p1]; //連想配列から値を取り出す
} 
var result = str.replace(reg, rep); //名前付き関数repの参照を渡す
alert(result); //「a=0 : b=1 : c=2」と表示

■受け取る引数の詳細

replaceに関数オブジェクトを渡すと、実行時に引数が渡される。その数は
グループ"()"の数 + 3
となっている。冒頭のサンプルを少し改造して以下のようなコードで引数の数と中身を確認してみる。サンプルコードでは1番目の引数をwhole、2番目をp1と決め打ちしていたのをargumentsオブジェクトを参照して全ての引数の中身を見れるようにする。
var named = {a:0,b:1,c:2}; //連想配列
var str = 'a=%a : b=%b : c=%c';//置換対象文字列
var reg = /%([a-c])/g; //正規表現オブジェクト生成
var result = str.replace(reg,
    function(){ //無名関数定義
        var args = '';
        for(var i=0; i<arguments.length; i++){ //引数の数だけループ
            args += i + ' : ' + arguments[i] + '\n';
        }
        alert(args);
        return named[arguments[1]]; //連想配列から値を取り出す
    }
);
alert(result); //「a=0 : b=1 : c=2」と表示
グループ数が1つの場合の受け取る引数の中身。グローバルオプション(g)付きなので3回マッチする。
1回目のマッチ
0 : %a
1 : a
2 : 2
3 : a=%a : b=%b : c=%c
2回目のマッチ
0 : %b
1 : b
2 : 9
3 : a=%a : b=%b : c=%c
3回目のマッチ
0 : %c
1 : c
2 : 16
3 : a=%a : b=%b : c=%c
次にグループ数を以下のように3つに増やして実行してみる。
var reg = /((%)([a-c]))/; //正規表現オブジェクト生成
グループ数が3つの場合の受け取る引数の中身
1回目のマッチ
0 : %a
1 : %a
2 : %
3 : a
4 : 2
5 : a=%a : b=%b : c=%c
2回目のマッチ
0 : %b
1 : %b
2 : %
3 : b
4 : 9
5 : a=%a : b=%b : c=%c
3回目のマッチ
0 : %c
1 : %c
2 : %
3 : c
4 : 16
5 : a=%a : b=%b : c=%c
次にグループ数を以下のように10個に増やして実行してみる。
var reg = /((%)([a-c]))()()()()()()()/; //正規表現オブジェクト生成
0 : %a
1 : %a
2 : %
3 : a
4 : 
5 : 
6 : 
7 : 
8 : 
9 : 
10 : 
11 : 2
12 : a=%a : b=%b : c=%c
以上の実験からまとめると、
  • arguments[0]はマッチした文字列全体
  • arguments[1] ~ arguments[arguments.length-3]はグループ単位でマッチした文字列。通常の後方参照で使う$1はaruguments[1]、$2はarguments[2]・・・となっている。参照できるグループの数に制限はない(詳しくは調べていないが100個以上のグループでも問題なく参照できる)。
  • arguments[arguments.length-2]はマッチした文字列が先頭から何文字目にあるか
  • arguments[arguments.length-1]は検索した文字列全体
ということになる。

■後方参照の展開のタイミング

 replaceの2番目の引数に無名関数を渡せると知らなかった頃、以下のようにマッチした文字列の後方参照$1を関数に渡して、処理した結果を置換文字列として使おうとしていたがうまく動かなかった。
これでは期待した動作はしない
var named = {a:0,b:1,c:2}; //連想配列
var str = 'a=%a : b=%b : c=%c'; //置換対象文字列
var reg = /%([a-e])/g; //正規表現オブジェクト生成
var rep = function(str){ //無名関数を変数repに格納
    return named[str]; //連想配列から値を取り出す
}
var result = str.replace(reg, rep("$1"));
alert(result); //「a=undefined : b=undefined : c=undefined」と表示
これは$1が実際のマッチ文字列に展開されるタイミングが期待より遅いのが原因。展開が行われるのはreplaceの2番目の引数「rep("$1")」が評価された後。つまりrepに格納された無名関数には文字列「$1」自体が渡される。alert(str)とすれば確認できる。その結果、「$1」をキーとして連想配列namedを検索することになるので未定義値undefinedが返されていた。
 冒頭のサンプルコードに似ているが、rep()だと関数への参照ではなくrep()の実行結果をreplaceに渡すことになるので意味がぜんぜん違う。
 
2008-03-03 02:33 | Javascript | Comment(0) | Trackback(1)
Safari、getElementsByClassNameをネイティブ実装
投稿日時 : 2007-12-26 13:43
 存在してそうで存在していないメソッドの代表格getElementsByClassNameがSafariでネイティブ実装されることになったようです。
 
Safari、JavaScript処理の高速化へ - builder by ZDNet Japan
 
 よく知らなかったんだけど、というか半分以上諦めてたんで調べもしてなかったわけですが、次期Firefox、Operaでもネイティブ実装することが決定しているらしい。
builder by ZDNet Japan
getElementsByClassNameは開発中のFirefoxやOperaでも実装されているため、これで次期Firefox、Opera、Safariの3者がそれぞれネイティブ実装を実現したことになる。
 
 これまではprototype.jsみたいなライブラリ使ったり、自分で作ったりしてがんばってたわけですが、結局無理矢理forとかforinで回してたんで検索エレメント数が増えてくると激しく重くなってたわけです。
 
prototype.js の document.getElementsByClassName は重い - IT戦記
 
これがネイティブ実装されることで劇的に処理速度が向上するという本当に嬉しいニュース。
 
 このニュースでは大御所のIEの情報がないようですがどうなってんだろう。そういえばJavascriptやり始めた当初、IEでclassがsetAttributeできないバグで発狂しそうになった覚えがある。
 
2007-12-26 13:43 | Javascript | Comment(0) | Trackback(0)
JavaScript実行速度はやはりOperaが最速
投稿日時 : 2007-12-22 11:29
 JavaScriptベンチマーク結果発表、OperaとSafari3が高速 | エンタープライズ | マイコミジャーナル
 
OperaがMicrosoftを提訴 - IEのバンドルは不法と主張」のエントリでも触れたがOperaはやはり軽い。IE7なんかと比べると体感速度として明らかに大きな差があるのは今回の最新のベンチマークの結果からも数値として明らかになった。
 
Coding Horror: The Great Browser JavaScript Showdown
browser benchmark

 
っていうかとりあえずIE7のstring処理がいくらなんでも遅すぎるだろw
で、Operaと僅差のSafariのページではOperaよりSafariの方がJavascriptが高速に動作すると謳っている。しかも、Javascript以外の項目でも、主要ブラウザの中でSafariが最も高速に動作すると書いてある。わかりやすいグラフも掲載されているが、「最速のブラウザはどれ? - GIGAZINE」の内容などと比べてもちょっとあやしいような。
 
アップル - Safari 3 パブリックベータ
borwser benchmark by apple

 
 まあJavascriptの実行速度に関しては、確かにもともとOperaとSafariに大きな差があるわけではないので、実験項目の設定の仕方によってはSafariの方が高速になる、ということもあるのかもしれない。しかし特定の条件下でのみ高速です、というのでは実際にユーザが使用したときの体感速度とはズレが生じてしまう。そのあたりを考えるとやはり第三者のベンチマーク結果の方が、実験項目も評価も公平で信頼できるものな気がする。
 一方、IEのシェアを一気に削ったFirefoxは、メモリのバカ食いや「もっさり」と形容される緩慢な動作でベンチマークではあまりいい評価を得られていないが、現在のFirefox2系の後継のFirefox3ではえらく動作速度が向上するらしい。最近beta2が公開されたようなのでどうせならFirefox3も含めたベンチマークが見たかった。
 
2007-12-22 11:29 | Javascript | Comment(0) | Trackback(0)
Lightboxをブログに導入する方法
投稿日時 : 2007-11-27 16:09
画像を画面遷移なしにポップアップ表示できるLightboxを導入してみました。
 
Lightbox JS
http://www.huddletogether.com/projects/lightbox/
 
Lightboxデモ(サムネイルクリックで実寸画像ポップアップ)
lightbox_demo
*ページのロードが完了する前にクリックすると画面遷移してしまいます。
 
FC2ブログで導入するには上記サイトでダウンロードできる「lightbox.css」と「lightbox.js」をちょっと編集する必要がある。まあ画像の参照先をFC2の仕様に合わせるだけなんですが。
 
各ファイルは以下のようにファイル名を変更してアップロードしてあります。lightbox.jsとlightbox.cssは編集後アップ。
lightbox.js → 変更なし
lightbox.css → 変更なし
overlay.png → lightbox_overlay.png
loading.gif → lightbox_loading.gif
close.gif → lightbox_close.gif
 
 
以下各ファイルの編集内容を示しますが、URLは各自の環境で読み替えてください。そのままコピペしても動くかもしれませんがやめてください。こちら側でファイル名変更したり削除すると動かなくなるだろうし。
 
■lightbox.cssの編集
lightbox.css : 18行目あたり
#overlay{ background-image: url(overlay.png); }
これを以下のように変更
#overlay{ background-image: url(http://blog-imgs-24.fc2.com/i/t/m/itmst/lightbox_overlay.png); }
 
 
lightbox.css : 24行目あたり
filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(
src="overlay.png", sizingMethod="scale"); }
これを以下のように変更
filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(
src="http://blog-imgs-24.fc2.com/i/t/m/itmst/lightbox_overlay.png", sizingMethod="scale"); }
 
 
■lightbox.jsの編集
lightbox.js : 38行目あたり
var loadingImage = 'loading.gif';
var closeButton = 'close.gif';
これを以下のように変更
var loadingImage = 'http://blog-imgs-24.fc2.com/i/t/m/itmst/lightbox_loading.gif';
var closeButton = 'http://blog-imgs-24.fc2.com/i/t/m/itmst/lightbox_close.gif';
 
 
■テンプレートの<head>~</head>に以下を追加
<script type="text/javascript" src="http://blog-imgs-24.fc2.com/i/t/m/itmst/lightbox.js"></script>
<link rel="stylesheet" type="text/css" href="http://blog-imgs-24.fc2.com/i/t/m/itmst/i/itmst/lightbox.css">
 
 
■本文中に以下のように画像をリンクする。
<a href="http://blog-imgs-24.fc2.com/i/t/m/itmst/e19_lightbox_demo.jpg" rel="lightbox"><img src="http://blog-imgs-24.fc2.com/i/t/m/itmst/e19_lightbox_demos.jpg" border="0" /></a>
ポイントはAタグの中に「rel="lightbox"」を追加することだけです。
 
 
2007-11-27 16:09 | Javascript | Comment(0) | Trackback(0)
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。