2
1

More than 1 year has passed since last update.

WSH/JScript は Underscore.js で関数プログラミングの夢を見るか?

Posted at

WSH/JScript + Underscore.js の環境で、オブジェクトが Recordset オブジェクト (ADO) かどうかをテストすることになった。

とりあえず一番最初に書いたバージョン

isAdoRecordset_take1.js
function isAdoRecordset( o ) {
	if( _.isUndefined( o.CursorType ) ) return false;
	if( _.isUndefined( o.LockType ) ) return false;
	if( _.isUndefined( o.BOF ) ) return false;
	if( _.isUndefined( o.EOF ) ) return false;
	if( _.isUndefined( o.Fields ) ) return false;
	return true;
}

typeof(o.CursorType)==='undefined'
の代わりに_.isUndefined 使っただけ。

まあこれはこれで悪くない。でももっとアンスコの機能を使おうよ。ってことで _.every ってのを使ってみることにした。そのために、テストするプロパティのキーを文字列のリストにする必要がある。配列リテラル書くのが面倒くさいので、perl の qw を再発明した。

perlQw.js
function perlQw(str){return str.split(' ')};

perlQw と _.every で実質ワンライナー化した第2版。

isAdoRecordset_take2.js
function isAdoRecordset( o ) {
	return _.every(perlQw('CursorType LockType BOF EOF Fields'),
	function($_){return ! _.isUndefined(o[$_])} );
}

これって「オブジェクトがリストのプロパティ/メソッドを持つか?」ということなので、一般化する。

hasProperties_take1.js
function hasProperties( o, props ) {
	return _.every(props,function($_){return ! _.isUndefined(o[$_])} );
}

hasPropatyes を使った第3版。

isAdoRecordset_take3.js
function isAdoRecordset( o ) {
	return hasProperties( o, perlQw('CursorType LockType BOF EOF Fields') );
}

やってることは同じだが関数化して名前を付けたことによってわかりやすくなった。気がする。
普段の自分ならここで一応完成なんだが、アンスコの「関数に引数を固定する」_.partial ってのを使ってみることにする。

_.partial は _.partial(function, *arguments) こんな感じの仕様で、関数に引数を部分適用した関数を返す。関数を返す関数だから「高階関数」(higher-order function)ってやつだな。

あ。俺の hasProperties、引数が「オブジェクト」「プロパティ」の順じゃないか。これはこれで自分的には正しいので、 _.partial 使うためだけに逆にしたくはない。
じゃあ、こうしよう。

isAdoRecordset_take4.js
var isAdoRecordset = _.partial( function(p,o){return hasProperties(o,p)}, perlQw('CursorType LockType BOF EOF Fields') );

ダッセー。_.partial が先頭の引数しか部分適用できないからだ。
だれか拡張とかしてないかな。誰もやってなさげなら自分でやるしか。
仕方ないのでコード読んでみる。
あ。
コメントに「_ acts as a placeholder by default」とか書いてある。どういうことだ?!
あ…

isAdoRecordset_final.js
var isAdoRecordset = _.partial( hasProperties, _, perlQw('CursorType LockType BOF EOF Fields') );

なるほどねー。うまいことできてる。
「_.partial が先頭の引数しか部分適用できない」はデマです。ごめんなさい。
作業用の変数や無名関数・引数を一掃したのでスッキリした。
とりあえずこれで完成としよう。

さてここで、hasProperties 初版をもう一度。

hasProperties_take1.js
function hasProperties( o, props ) {
	return _.every(props,function($_){return ! _.isUndefined(o[$_])} );
}

はいはい、作業用の無名関数や ! による論理反転とかがあるのが気になってきた。
まずは論理反転から。「プロパティ参照の結果が undefined ではない」じゃなくストレートに「オブジェクトのプロパティか」にすれば正論理だ。

てなわけで第2版。

hasProperties_take2.js
function hasProperties( o, props ) {
	return _.every(props,function($_){return $_ in o} );
}

ああ、待てよ?「オブジェクトのプロパティか」とかアンスコにあるんじゃねえか?
…あった。_.has(object, key)
_.every から呼べるように第1引数を部分適用すれば無名関数を無くせる。

hasProperties_final.js
var hasProperties = function( o, props ) {return _.every( props, _.partial( _.has, o ) )};

こちらもこれで完成だ。わざわざ _.partial 使うのはやりすぎのような気もするが、今回はまあ Underscore.js の活用演習ってことなので。

これでいいのだ。

2
1
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1