Macromedia Flash非公式テクニカルノート

配列をランダムに並替えるメソッドを定義する[上級テクニック]

ID: FN0212002 Product: Flash

Platform: All
Version: 5.0 and Above

このノートでは、Arrayオブジェクトにエレメントをランダムに並替えるメソッドを定義する上級者向けのサンプルを、ご紹介します。

1. Arrayオブジェクトにメソッドを定義する
'new'演算子を使ってインスタンスを生成するオブジェクト(クラス)には、'prototype'プロパティがあります。この'prototype'プロパティに'function'を設定すると、そのオブジェクト(クラス)にメソッドを追加して定義することができます。

Arrayもそうした定義のできるオブジェクトのひとつです。ArrayオブジェクトにメソッドnewMethodを定義するには、つぎのようなスクリプトを記述します。

Array.prototype.newMethod = xNewMethod;
function xNewMethod () {
  // メソッドの行う処理
}

あるいは、名前のない関数を使って定義することも可能です。

Array.prototype.newMethod = function() {
  // メソッドの行う処理
}

'function'内でオブジェクトインスタンスのプロパティやメソッドにアクセスするには、必ず'this'を参照しなければなりません。'this'がないと、'function'の定義されたタイムラインが参照されてしまいます。

2. 配列エレメントをランダムに並替える
Arrayオブジェクトに対して、エレメントをランダムに並替えるメソッドshuffleを定義します。

考え方は、カードのシャッフルと同じです。カードをまとめて手にしたら、好きな位置から1枚抜いてテーブルの上に重ねます。つぎのカードも同じように、1枚抜いてはテーブルのカードに積重ねます。それを繰返して、手に残った最後の1枚をテーブルの山の一番上に重ね終わったとき、ランダムに並替えたカードの山ができあがる訳です。

// フレームアクション
// 配列のエレメントをランダムに並替える
// 引数: なし
// 戻り値: なし
Array.prototype.shuffle = function() {  //[1]Arrayオブジェクトにメソッド定義
  var i = this.length;
 //[2]配列の長さを取得
  while (i) {
 //[3]配列インデックスの最後から順にループ処理
    var j = Math.floor(Math.random()*i);
 //[4]並替え対象のインデックスをランダムに決定
    
//[5]対象インデックスとランダムなインデックスで値を入替える
    var t = this[--i];
    this[i] = this[j];
    this[j] = t;
  }
}

スクリプトとしては、比較的シンプルです。

[1]Arrayオブジェクトの'prototype'プロパティに、メソッドshuffleを定義します。名前のない関数による設定です。

[2]まず配列の長さを取得します。エレメントの数(カードの枚数)をもとに、取出すランダムなエレメントのインデックス番号(カードの位置)を計算するためです。

[3]配列のエレメントすべてをループ処理します。ループ条件は、論理式でなく変数iになっています。変数が数値の場合、0以外の値は'true'と評価されます。つまり、カードがすべてなくなるまで、抜出しては重ねる処理を行う訳です。

[4]並替える対象となるインデックス番号(抜出すカードの位置)を、ランダムに決定します。返される値は、0からi-1までの整数です。変数iの初期値は配列の長さで、配列の最後のエレメントのインデックスより1大きいです。したがって、0から最後のインデックス番号までの値が、ランダムで返されます。なお、偏りなくランダムに並替えるための基本的な考え方については、「配列を偏りなくランダムに並替える」をご参照ください。

'while'ループ内の処理で、iの値は1ずつ減算されます。これは、手元のカードから1枚ずつ抜出すことにより、カードの数が減っていくことに対応します。

[5]ランダムに決められたインデックス番号のエレメントを抜出して、配列の最後のエレメントから順に差替えていきます。変数iの初期値は配列の長さですので、最後のエレメントのインデックスは--iで1減算しています。

つまり、配列を実際には(手元とテーブルの)2つに分けず、最後のエレメントから順にランダムなインデックスのエレメントと交換するという処理をしています。しかし、取出し方がランダムですので、処理の結果は2つに分けた場合と異なりません。

なお、ランダムなインデックスjと並替え対象のインデックスiとが、同じ数値になる場合が考えられます。上記のスクリプトでは、このときもエレメントを抜出して、同じ位置に戻すという処理をすることになります。これは、問題はないものの、無駄な処理に見えます。けれども、この場合を条件判定して処理をスキップするより、条件判定自体を行わない上記のスクリプトの方がスピードは勝ります。

_____

作成者: 野中文雄
協力者: rio
作成日: 2002年12月15日


Copyright © 2001-2006 Fumio Nonaka.  All rights reserved.