ふとライブラリを更新したいという衝動にかられ久しぶりに目を通してみたんですが、なんかこうしっくり来ない。
その辺が有耶無耶になってなかなか進まないわけですが、これはなんとなく「appicationとwebプログラムとの違い」だと思います。webで提供するクラス(オブジェクト)というのはやはり癖があるのだと・・・そう思うわけです。それが何であるかは自分でも分かりません。
2次元ベクトルVectorクラスのコンストラクタ、以下に対して
function Vector(){ if( arguments.length == 2 ){ this.x = arguments[0]; this.y = arguments[1]; }else{ this.x = arguments[0].x ; this.y = arguments[0].y ; } }
これに加算(add)、減算(sub)、スカラー倍(scale)のメソッドを加えていきます。あなたはどの実装方法が好みでしょうか?
また、webプログラムではどれが良いのでしょうか?(僕は知りません)
// 1 Vector.prototype.add = function( p ){ this.x += p.x ; this.y += p.y ; }; Vector.prototype.sub = function( p ){ this.x -= p.x ; this.y -= p.y ; }; Vector.prototype.scale = function( v ){ this.x *= v ; this.y *= v ; };
// 2 Vector.prototype.add = function( p ){ return new Vector( this.x+p.x, this.y+p.y ); }; Vector.prototype.sub = function( p ){ return new Vector( this.x-p.x, this.y-p.y ); }; Vector.prototype.scale = function( v ){ return new Vector( this.x*v, this.y*v ); };
// 3 Vector.add = function( p0, p1 ){ return new Vector( p0.x+p1.x, p0.y+p1.y ); }; Vector.sub = function( p0, p1 ){ return new Vector( p0.x-p1.x, p0.y-p1.y ); }; Vector.scale = function( v, p ){ return new Vector( p.x*v, p.y*v ); }; Vector.prototype.add = function( p ){ this.x += p.x ; this.y += p.y ; }; Vector.prototype.sub = function( p ){ this.x -= p.x ; this.y -= p.y ; }; Vector.prototype.scale = function( v ){ this.x *= v ; this.y *= v ; };
とりあえず3つ挙げてみました。機能的にはなんら支障はないですが、これらにはそれぞれ長所と短所があります。
var a = new Vector( 0, 0 ); var b = new Vector( 1, 2 ); var c = new Vector( 3, 5 ); var k = 3 ;
使用例として、ここに↑のようなベクトルとスカラー(定数)を用意し d=(a+b)*k-c
となるようなベクトルdを計算してみたいと思います。
但し、どのベクトルに関しても中身(インスタンス変数)を変更してはいけないという条件付きです(後で使用するなどこの条件が付くことは多い)。
// 1 var d = new Vector( a ); d.add( b ); d.scale( k ); d.sub( c );
// 2 var d = a.add( b ).scale( k ).sub( c );
// 3 var d = Vector.add( a, b ); d.scale( k ); d.sub( c );
今までは1でやってましたが、はっきり言って2が一番楽で僕の好みです。
しかし、メモリを無駄に消費するので現在のライブラリでは適用しませんでした。
そこで1の改良版でスタティックなメソッドを追加した3が出てくるのですが、なんかあんまり変わらないような気がしますか?
使うなり経験してみれば分かりますが、var d = Vector.add( a, b );
のようにいきなりインスタンスを生成出来るのは機能性としてはでかいです。また↓ようにも書けます(2と同じです)。
// 3 var d = Vector.sub( Vector.scale( k, Vector.add( a, b ) ), c );
しかし、JavaScriptではスタティックなメンバが継承されないなどの理由で僕は3の方法はあまり好きではありません。
なんかどれもしっくりこないです。ということで Java言語の Tuple3dクラスを参考にすることにしました(4)。
// 4 Vector.prototype.add = function( p0, p1 ){ if( arguments.length == 1 ){ this.x += p0.x ; this.y += p0.y ; }else{ this.x = p0.x+p1.x ; this.y = p0.y+p1.y ; } }; Vector.prototype.sub = function( p0, p1 ){ if( arguments.length == 1 ){ this.x -= p0.x ; this.y -= p0.y ; }else{ this.x = p0.x-p1.x ; this.y = p0.y-p1.y ; } }; Vector.prototype.scale = function( p0, p1 ){ if( arguments.length == 1 ){ this.x *= p0 ; this.y *= p0 ; }else{ this.x = p1.x*p0 ; this.y = p1.y*p0 ; } };
1の拡張版とでもいいますでしょうか。このTuple3dクラス(メンバにはzも含みます)は3Dに使用するもので大量に生成することが前提で作成されています。兎に角メモリに重点を置いているということです。
それに機能性を加えて a.add( b, c );
という書き方を可能にさせているようです。
使用例は1の方法と全く変わりませんが、既存のベクトルdに関して↓のようにも出来るようになっています。
// 4 d.add( a, b ); d.scale( k ); d.sub( c );
はぁ・・・結局のところどれが良いのでしょうか?
もうちっと真面目にプログラム設計の授業聞いとけば良かった・・・