« 過去の記事… | トップページ | C4DのC.O.F.F.E.E.のループwhile(){...} »

C4DのC.O.F.F.E.E.のループfor(){...}

C.O.F.F.E.E.でのループで処理する為の1つのfor()。他にはwhile()があります。

あえて取り上げる事ではないのですが…

参考のまで…

for()とは…

for(カウンター変数の初期化 ; ループの条件 ; カウンター変数の増分){処理}

ですね。

では、次のスクリプトを

/*
for()のテスト
2009.5.21
*/


main(doc , op)
{
  var i;

  for(i = 0 ; i < 10 ; i++){
    println(i);
  }

  println("--end--");
}

0
1
2
3
4
5
6
7
8
9
--end--

コンソールには、このように表示されます。

単純明快なfor()での10回ループですね。

と言いましたが、これは結果10回ループすると言うだけです。
正確には、「カウンター変数が0から1づつ増えて10未満の間ループする。」ですよね。
増分が2であれば、5回のループになります。
要するに、回数が問題なのではなく、while()同様「条件が満たされている間ループする。」と言う事ですね。

では、ループが終了してfor()から出た後のカウンター変数の値は?
9それとも10

/*
for()のテスト
2009.5.21
*/


main(doc , op)
{
  var i;

  for(i = 0 ; i < 10 ; i++){
    println(i);
  }

  println("loop out : " , i);

  println("--end--");
}

0
1
2
3
4
5
6
7
8
9
loop out : 10
--end--

ループ終了後、カウンター変数は10です。
カウンター変数が10になったので、ループ条件[カウンター < 10(10未満)]から外れたのでループの処理をしないでループから出たのですね。

では、次にループの途中でループの処理を終了する。

/*
for()のテスト
2009.5.21
*/


main(doc , op)
{
  var i;

  for(i = 0 ; i < 10 ; i++){
    println("befor : " , i);

    if(i == 5)break;//ループカウンターが5になったらループを脱出

    println("after : " , i);
  }

  println("loop out : " , i);

  println("--end--");
}

befor : 0
after : 0
befor : 1
after : 1
befor : 2
after : 2
befor : 3
after : 3
befor : 4
after : 4
befor : 5
loop out : 5
--end--

breakでループを脱出します。

after : 5

が、コンソールには表示されていません。

ループ処理をbreak前に置けば、処理をしてからループを脱出します。
逆にbreakの後ろに置けば、処理をせずに脱出します。

breakの位置に注意が必要です。

脱出後のループカウンターは5です。
6ではありません。

次は、ループの処理を1回飛ばします。

/*
for()のテスト
2009.5.21
*/


main(doc , op)
{
  var i;

  for(i = 0 ; i < 10 ; i++){
    println("befor : " , i);

    if(i == 3)continue;//ループカウンターが3になったら処理を飛ばす。

    println("after : " , i);
  }

  println("loop out : " , i);

  println("--end--");
}

befor : 0
after : 0
befor : 1
after : 1
befor : 2
after : 2
befor : 3
befor : 4
after : 4
befor : 5
after : 5
befor : 6
after : 6
befor : 7
after : 7
befor : 8
after : 8
befor : 9
after : 9
loop out : 10
--end--

continueで処理を飛ばします。

after : 3

が表示されていません。

continue以降のループ処理が飛ばされます。
ループカウンターがcontinueによって増分され、ループ処理を続行しているわけではありません。
これもcontinueの位置に注意が必要です。

これがfor()/break/continueの基本です。

では、次にループ処理中にカウンター変数を変更した場合は?

/*
for()のテスト
2009.5.21
*/


main(doc , op)
{
  var i;

  for(i = 0 ; i < 10 ; i++){
    println("befor : " , i);

    if(i == 2)i += 5;//ループカウンターが2になったらカウンターに+5する。

    println("after : " , i);
  }

  println("loop out : " , i);

  println("--end--");
}

befor : 0
after : 0
befor : 1
after : 1
befor : 2
after : 7
befor : 8
after : 8
befor : 9
after : 9
loop out : 10
--end--

カウンター変数は処理の途中で変更され、結果ループの回数が変わります。

for()とカウンター変数は対になっているわけではなく、カウンター変数自体は独立しています。
for()ではカウンター変数の増分をループ処理の最後に行っているだけに過ぎません。

ここでも注意が必要です。
ループ処理中にカウンター変数を誤って変更してしまうと、ループの回数が変わってしまいます。

次に、繰り返しの条件。

/*
for()のテスト
2009.5.21
*/


main(doc , op)
{
  var i;

  for(i = 1 ; i == 10 ; i++){
    println(i);
  }

  println("--end--");
}

1
2
3
4
5
6
7
8
9
10
--end--

これも10回ループです。ただしカウンター変数は1~10です。

条件式が [カウンター変数 == 10] になっています。

ここにも注意が必要です。
カウンター変数が整数で、初期化が10よりも小さく、増分が1なので、必ず10になることでしょう。

でも、増分が2や3だった場合10をすり抜ける場合があります。
カウンター変数が実数で増分が半端なときにもすり抜けます。
ま、カウンター変数はなるべく整数にするんでしょうが…
すり抜けてしまうと無限ループになってしまいます。

注意!
余談ですが、間違っても次のようなタイプミスをしないように。無限ループになります。

  for(i = 1 ; i = 10 ; i++){
i = 10は、i が10なのか比較するのではなく、i に10を代入するわけだね。
その為、ループ内で i は常に10のままになるからです。

話は戻りますが、この場合だと[カウンター変数 <= 10]このようにする方が良いでしょう。

  for(i = 1 ; i <= 10 ; i++){

でもこの場合でも、増分が(-)負の値なら無限ループになるわけだね。
じゃあ、[カウンター変数 >= 1 && カウンター変数 <= 10]とすると、より確実だね。

  for(i = 1 ; i >= 1 && i <= 10 ; i++){

でも、このように指定しなさいと言うわけではありませんよ。

では、このループ条件は、ループの開始に固定されループ処理の最初に比較されるのか?
それとも、ループの条件はループ処理中にでも変更できるのか?

/*
for()のテスト
2009.5.21
*/


main(doc , op)
{
  var i;
  var max = 20;//繰り返し条件用の最大値

  for(i = 0 ; i < max ; i++){
    if(i == 10)max = 30;
    println(i);
  }

  println("--end--");
}

ループ開始時は[ループカウンター < 20]です。
カウンターが10になった時[ループカウンター < 30]になり、ループは結果30回になります。

ループの条件は開始時に固定されるわけではありません。
ループ1づつ処理前に単純に在るがまま比較されるだけでした。

このループの条件は、ただ単純に条件であって、条件式の結果のみで処理が実行されます。

for(カウンターの初期化 ; [TRUE or FALSE] ; 増分){処理}

要するに、ループの条件というよりも、単純に条件式で、その結果がTRUEだったらループ処理を実行するわけです。

だから、次のスクリプトも問題はありません。

/*
for()のテスト
2009.5.21
*/


main(doc , op)
{
  var i;
  var res = TRUE;
  var a = 0;

  for(i = 0 ; res ; i++){
    if(a > 10)res = FALSE;
    println(i);
    a++;
  }

  println("--end--");
}

ループの条件式[res]がループ開始時にTRUEなのでループ開始。
for()のカウンターは1づつ増加していますが、for()に直接関係の無い別のカウンター[a]が10より多くなった11で[res]がFALSE。
その結果、カウンター11の処理をしないでループ処理を終了。

この事から言える事は、条件式も独立しているわけです。
for()で初期化したカウンター変数に直接関係しなくてもいいわけです。

この条件式、[TRUE/FALSE]でループ[する/しない]となると、[on/off]で反応する単なるループフラグのようだね…

次に、カウンタ変数の増分です。

説明するほどではないのでさらりと。

i++ ++i 1加算(インクリメント)
i = i + n i += n n加算
i-- --i 1減算(デクリメント)
i = i - n i -= n n減算

/*
for()のテスト
2009.5.21
*/


main(doc , op)
{
  var i;

  //加算
  println("--- [i++] ---");
  for(i = 0 ; i < 10 ; i++)println(i);

  println("--- [++i] ---");
  for(i = 0 ; i < 10 ; ++i)println(i);

  println("--- [i = i + 2] ---");
  for(i = 0 ; i < 10 ; i = i + 2)println(i);

  println("--- [i += 2] ---");
  for(i = 0 ; i < 10 ; i += 2)println(i);

  //減算
  println("--- [i--] ---");
  for(i = 10 ; i > 0 ; i--)println(i);

  println("--- [--i] ---");
  for(i = 10 ; i > 0 ; --i)println(i);

  println("--- [i = i - 2] ---");
  for(i = 10 ; i > 0 ; i = i - 2)println(i);

  println("--- [i -= 2] ---");
  for(i = 10 ; i > 0 ; i -= 2)println(i);
}

次は、カウンター変数を2個使ってみます。

2個って?
スプラインオブジェクトを選択して、次のスクリプトを試してね。

/*
スプラインオブジェクトのポイントとハンドルを表示
2009.5.21
*/


main(doc , op)
{
  if(!instanceof(op , SplineObject))return;
  var pts = op->GetPoints();
  var tgs = op->GetTangents();
  var ptc = op->GetPointCount();

  var i , j;

  for(i = 0 , j = 0 ; i < ptc ; i++ , j += 2){
    print(i , " P : ");
    print(pts[i] , " L : ");
    print(tgs[j] , " R : ");
    println(tgs[j + 1]);
  }
}

ポイント配列変数は、1個づつ増加させる。
ハンドル配列変数は、ポイント1個に対して2個あるので2づつ増加させる。
カウンター[i]は1づつ加算
カウンター[j]は2づつ加算
増分の違う2個のカウンターを同時に扱うことができます。
初期化、増分の区切りは(,)コンマです。順番は関係ありません。

では、ここでスクリプトやプログラミングを始めたばかりの方のfor()の固定概念を吹き飛ばすスクリプトです。

/*
for()のテスト
2009.5.21
*/


main(doc , op)
{
  var res = TRUE;
  var a = 0;

  for(println("loop start") ; res ; println("ok")){
    if(a > 10)res = FALSE;
    a++;
  }

  println("last a:" , a);
  println("--end--");
}

「こんなのエラーに決まってるじゃないか。」と思った方もいることでしょう。

for()をこの様に使う方はいないでしょうが、実行しても問題はありません。
ループ処理を行って、最後まで処理をして終了します。

loop start
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
last a:12
--end--

for()の説明のよくある書き方は、この記事の冒頭にある説明、

for(カウンター変数の初期化 ; ループの条件 ; カウンター変数の増分){処理}

が一般的ですが、本当はこんな感じです。

for(ループ開始直前の1回だけ処理 ; 条件式 ; ループ処理毎の最後の処理){処理}

乱暴な言い方ですが、
カウンターの初期化/カウンターの増分なんて関係ないのです。
ループが始まる前、ループ処理毎の最後に実行したいことを記入できるのです。

では、この事をふまえて、最後にグループ内の子オブジェクトをfor()を使って捜査し名前を表示します。

For_01

通常、繰り返し回数が未定なので、この場合while()で捜査します。
ではwhile()ループの場合。

/*
while()でのグループ内の捜査。オブジェクトの名前を表示
2009.5.21
*/


main(doc , op)
{
  var a = op->GetDown();//子オブジェクトの取得

  while(a){//オブジェクトが存在する間ループする。
    println(a->GetName());//オブジェクトの名前を表示

    a = a->GetNext();//次のオブジェクトを取得
  }

  println("---loop end---");
}

オブジェクトがなくなるまで、次のオブジェクトを繰り返し取得し名前を表示します。

では、同じようにfor()で実行してみます。

/*
for()でのグループ内の捜査。オブジェクトの名前を表示
2009.5.21
*/


main(doc , op)
{
  var a;

  for(a = op->GetDown(); a ; a = a->GetNext()){
    println(a->GetName());//オブジェクトの名前を表示
  }

  println("---loop end---");
}

上のwhile()ループ同様、処理を完了します。
ループ初期の処理、ループフラグ、ループ毎最後の処理、が1行で記述できスッキリしています。
でも、わかり辛いですかね。

本当の最後に…
for()のフローチャートはこんな感じかな?

for(繰り返し開始処理 ; 条件式 ; 繰り返し終了処理){繰り返し処理}

For_02

|

« 過去の記事… | トップページ | C4DのC.O.F.F.E.E.のループwhile(){...} »