Reactive Extensions で非同期処理を簡潔に記述するでは「キー入力に応じて補完候補を表示する」「ただし補完候補はAjaxで非同期に取得する」という
いまどきのWebアプリケーションにならあって当然の機能が、Reactive Extensions (Rx)を使うことであたかも普通のリスト処理のように記述できることを示しました。
入力補完は例としては単純で分かり易いものの、
もう少し別の例も欲しいところです。
という訳で、 Rx を使うことでコナミコマンドを実装してみましょう。
var lastKeyCodes = [];
var validKeyCodes = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65];
$(window).keydown(function (e) {
lastKeyCodes.push(e.keyCode);
if (validKeyCodes.length < lastKeyCodes.length)
lastKeyCodes.shift();
if (lastKeyCodes.toString() == validKeyCodes.toString())
alert('***!');
});
「最新のキー入力10個がコナミコマンドに合致するなら」という考えで書いてみました。
まあこんなものでしょう。
var lastKeyCodes = [];
var validKeyCodes = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65];
var konamiStream =
$(window)
.toObservable('keydown')
.Select(function (e) {return e.keyCode;});
.Select(function (c) {
lastKeyCodes.push(c);
if (validKeyCodes.length < lastKeyCodes.length)
lastKeyCodes.shift();
return lastKeyCodes.toString() == validKeyCodes.toString();
});
konamiStream.Subscribe(function (b) {
if (b)
alert('***!');
});
回答1を直訳しました。
キー入力の処理と実際に行いたい処理を分離できているという点では良いのですが、toObservable
だの Subscribe
だののために若干記述が冗長になっています。
var validKeyCodes = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65];
var konamiStream =
$(window)
.toObservable('keydown')
.Select(function (e) {return e.keyCode;});
.BufferWithCount(validKeyCodes.length, 1)
.Select(function (cs) {
return cs.toString() == validKeyCodes.toString();
});
konamiStream.Subscribe(function (b) {
if (b)
$('#result').prepend('<div>' + b + '</div>');
});
回答1と回答2の真の問題点は最新のキー入力(lastKeyCodes
)という状態を管理していることです。
そのために本来やりたいこと(コナミコマンドの入力判定)とは関係のないものが途中に混ざってしまい、
回りくどいコードになっています。
しかしBufferWithCount
(C# では Buffer
)
を使えば明示的に状態を管理することなく「最新のデータn個」単位で処理を記述することができます。
これで本来やりたいことをすっきりと記述できるようになりました。
やりましたね。
BufferWithCount
の最初の引数には一度に処理したいデータの個数を指定し、
二番目の引数では各処理の間でスキップするデータの個数を指定します。
今回の例の場合は BufferWithCount(validKeyCodes.length, 1)
ですが、BufferWithCount(3, 3)
とすれば「3個単位でデータを処理する」こともできます。
例えば xs = [1, 2, 3, 4, 5, 6, 7, ...]
というデータのシーケンスがあったとすると、
xs.BufferWithCount(3, 1)
で[[1, 2, 3], [2, 3, 4], ...]
xs.BufferWithCount(3, 3)
で[[1, 2, 3], [4, 5, 6], ...]
というシーケンスに変換できるということです。
Rx には他にも便利なメソッドが山のようにあるので、
これを駆使すればより複雑な処理も簡潔に書けるようになるでしょう。