コンピュータなんてものは詰まるところ、条件分岐とgotoの塊なんです。決められたとおりにしか動きませんし、決められたことなら何でもできます。プログラミング言語ではif, while(for)が使えればつまるところ何でもできますが、それはとても見づらいプログラムなんですね。
例えばコントローラーを操作するプログラムを考えましょう。 コントローラーの値を取得するために、for文で取得するとします。
for(int i = 0; i < controller_number; i++) {
state = controller[i].getState();
for(int j = 0; j < state.button_number; j++) {
if((state.button[j] & 0x80) == 0x80) {
controller[i].inputButton(j);
}
}
}
controllerはControllerクラスの配列で、そのサイズがcontroller_numberで取得できているとします。
続いて、stateはcontrollerのメソッドgetStateで取得した値で、ボタンのON/OFFやアナログスティックの値を記録しています。これはDirectInputを想定しています。
ボタンが押されたら、0x80でマスクしてそれが0x80と一致するかどうかチェックして、一致していたらコントローラーのボタンメソッドを呼び出します。もちろんこれはこれでいいのですが、このようなコードは冗長だと思いませんか。 毎回毎回おきまりのint i = 0してi++とおきまりのコードを書く。 ぱっと見、このループが特別な処理のためのループなのか、ある配列などを反復して処理しているだけなのか分かりません。
もしもC#のforeachを使って次のように書けたら幸せでしょう。
foreach(Controller controller in controllers) { // iがない
foreach(State.Button state_button in controller.getState().getButton()) {
// このState.Buttonは実質byteである
if((state_button & 0x80) == 0x80) {
controller.inputButton(j);
}
}
}
iとかjとかあるコードよりもぱっと見て反復していることが分かりよいでしょう。
また、iとかjとかに悩まされることもありません。それでは自分で実装してみましょう。foreachを使うには、foreach用に System.Collections.IEnumerableインターフェースを実装する必要があります。 IEnumerableは public System.Collections.IEnumerator GetEnumerator() を実装すれば良いです。これはforeach(★ in ●)の(●)にあたるオブジェクトのIFです。 IEnumeratorは状態をリセットするpublic void Reset()と、 次へ進むpublic bool MoveNext()。ただし繰り返すときだけtrueを返す。 そして現在の状態(★)を返すobject System.Collections.IEnumerator.Currentプロパティが必要です。
次の例ではMyClassにあるプロパティary (int型配列)をクラスそのものを使ってforeachで取得できるようにしたものです。
using System.Timers;
class Program {
public static void Main() {
MyClass myclass = new MyClass();
myclass.setSeriesData();
foreach(int x in myclass) {
System.Console.WriteLine("{0}", x);
}
}
}
class MyClass : System.Collections.IEnumerable {
int[] ary;
const int MAX_SIZE = 32;
public MyClass() {
ary = new int[MAX_SIZE];
}
public void setSeriesData(){
for(int i = 0; i < MAX_SIZE; i++) {
ary[i] = i;
}
}
public System.Collections.IEnumerator GetEnumerator() {
return new MyClassEnum(this);
}
class MyClassEnum : System.Collections.IEnumerator {
int current;
MyClass myclass;
public MyClassEnum(MyClass myclass) {
this.myclass = myclass;
}
object System.Collections.IEnumerator.Current {
get {
return myclass.ary[current];
}
}
public void Reset() {
current = 0;
}
public bool MoveNext() {
current++;
return current < MyClass.MAX_SIZE;
}
}
}
0 件のコメント:
コメントを投稿