2007年 11月 30日

Ruby1.9 のシンボルキーハッシュリテラルの簡易記法

p({ foo: "abc", bar: "def" })

こうかけるやつなんだけど、JS みたいなノリで

p({ foo : "abc", bar : "def" })

と書くとパースエラーになる。


シンボルキーと他のキーは同時に書けるけど、ちょっと混乱するかもしれない

p({ foo: "abc", bar: "def", "baaa" => "aaa" })

それに、うえのように書いても "baaa" と :baaa は違うオブジェクトなので

p foo[:baaa]  #=> nil
p foo["baaa"] #=> "aaa"

Ruby で arguments.callee ってどうやるんだろ

self == -> { self }.()

(もちろんまちがってないけど) true だしなぁ。

引数かえられないけど redo で現在のメソッドを最初からやりなおすのはできる……

Binding#callee とか実装できないかなぁ (実行中の Method/Proc をかえす)

jAutoPagerize / はてなスター

フィルタの適用方法を変えて、importNode されたノード個々に対してフィルタを適用するようにした。本家と挙動が違うかもしれない。(フィルタ書いたことなくてよくわからない)

でもってはてなスターがロードされているページではデフォでスターをロードするようにした。Hatena.Star に特定ノード以下に制限してロードするのがあったのでそれつかってみた。(はてダのクイックページャのソースからたどった)

GM の config

直書きだと、バージョンアップするたびにコピペが発生してめんどい。どうにかしたい。

最近のまいぶーむ -> MochiKit Deferred の再発明

MochiKit の Deferred がよくできてるなぁと実感する。Deferred チェイン中に Deferred return するとチェイン中断して……みたいなのがかっこいい (そういうのを Deferred 一個で統一してできるようにしているので MochiKit Deferred は結構サイズが大きい)。でも API がどうしても好きになれない。jQuery に移植したい。

でも慣れないとよくわからないよなぁ……(setTimeout の挙動 (ブラウザの実行キューのほげほげ) とかをわかってないと、Deferred つくってるところで setTimeout(, 0) してて、なにこれ? っておもうことになる。)

Deferred チェイン。

MochiKit の実装はもっとシンプルにできそうなのでつくってみた。でもいろいろ頭悪くて考えきれてないと思う……

例えばこういう風に書けるようにする。

wait(1).
next(function (e) {
	log([1, e]);
	return wait(1).next(function (e) {
		log([2, e]);
		return wait(2).next(function (e) {
			log([3, e]);
		});
	});
}).
next(function (e) {
	log(4);
	return "555";
}).
next(function (e) {
	log(e);
})

末尾再帰っぽいの (return call ってのが tailcall っぽくてよくないですか><)

next(function () {
	log("start");
}).
next(function () {
	function pow (x, n) {
		function _pow (n, r) {
			if (n == 0) return r;
			return call(_pow, n - 1, x * r);
		}
		return call(_pow, n, 1);
	}
	return call(pow, 2, 10);
}).
next(function (r) {
	log([r, "end"]);
}).
error(function (e) {
	alert(e);
})

実装

function Deferred () { this.init.apply(this) }
Deferred.prototype = {
	init : function () {
		this.callback = {
			ok: function (x) { return x },
			ng: function (x) { throw  x }
		};
		this._next    = null;
	},

	next  : function (fun) { return this._post("ok", fun); },
	error : function (fun) { return this._post("ng", fun); },
	call  : function (val) { return this._fire("ok", val); },
	fail  : function (err) { return this._fire("ng", err); },

	cancel : function () {
		this._next = null;
	},

	_post : function (okng, fun) {
		this.callback[okng] = fun;
		this._next = new Deferred();
		return this._next;
	},

	_fire : function (okng, value) {
		var self = this;
		var next = "ok";
		try {
			value = self.callback[okng].call(self, value);
		} catch (e) {
			next  = "ng";
			value = e;
		}
		if (value instanceof Deferred) {
			value._next = self._next;
		} else {
			setTimeout(function () {
				if (self._next) self._next._fire(next, value);
			}, 0);
		}
	}
};

function wait(n) {
	var d = new Deferred();
	var t = new Date();
	setTimeout(function () {
		// 実際にかかった時間をコールバック
		d.call((new Date).getTime() - t.getTime());
	}, n * 1000)
	return d;
}

function ps () {
	var d = new Deferred();
	setTimeout(function () { d.call() }, 0);
	return d;
}

function next (fun) {
	return ps().next(fun);
}

function call (f, args) {
	args = Array.prototype.slice.call(arguments);
	f    = args.shift();
	return next(function () {
		return f.apply(null, args);
	});
}

なかなかカッコいい感じがする。もうちょいいじる。

next は this ではなくて、次の Deferred (this が準備できたらよばれる Deferred // _next) をかえす。これでずっとチェインしていく。もしコールバックが Deferred をかえしたら、その Deferred に _next をわたしてあげる。_next は継続 (ていっていいのかな。継続をちゃんと理解していない)

スタック消耗してたのを修正した

Deferred loop

function loop (o, fun) {
	var begin = o.begin || 0;
	var end   = o.end;
	var step  = o.step || 1;
	var ret;
	return next(function () {
		function _loop (i) {
			if (i < end) {
				ret = fun.call(this, i, step);
				return call(_loop, i + step);
			} else {
				return ret;
			}
		}
		return call(_loop, begin);
	});
}


next(function () {
	var hoge = 0;
	return loop({end:50000, step:50}, function (n, step) {
		for (var i = 0; i < step; i++) {
			hoge += n+i;
		}
		return hoge;
	});
}).
next(function (e) {
	log(e);
	log("end");
}).
error(function (e) {
});

50000回のループを50ずつ実行する。