【ArtistでもMELが書きたい】Maya MEL入門(10)【for文】

# はじめに
この記事は、3DCG業界で働く**Artist向けのMEL入門記事**です。
普段の業務を効率化したいけど、MELの書き方が分からない、調べたけどイマイチ分からなかった、そんな方向けに書きました。
本記事は「for文を使う」をゴールとします。

# 環境
Autodesk Maya 2022.2

# 前回のあらすじと今回へのつなぎ
[前回](https://qiita.com/Hum9183/items/be7d648eb760f3aab2ca)は便利なコマンドやフラグを紹介しました。
さて本記事も10回を迎え、第1回のときとは比べ物にならないくらいたくさんのことを学び、MELで基本的なことは一通りできるようになったと思います。
今回は練習問題として、「**シーン内のすべてのジョイントの名前を、prefixが"jnt_"の名前にリネームするMEL**」を開発していきます。
(prefixとは接頭辞のことです。例を上げると、head骨に接頭辞"jnt_"をつけると「**jnt_head**」になるイメージです)

# 早速書いてみる
シーン内のすべてのジョイントを取得は、
```
string $allJoints[] = `ls -type "joint"`;
```
で取得できますね。

リネームには`rename`コマンドを使用します。
```
rename "beforeName" "afterName";
```
試しに使ってみましょう。
joint1がシーン内にある状態で、
![MEL_learning01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/f76bd08e-d2f5-9ff5-3661-c572b721e399.png)
```
string $jnt = "jnt_";
rename "joint1" ($jnt + "joint1");
```
上記を実行します。
![MEL_learning02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/46ae4e84-8948-b893-b4de-78ed73d75b39.png)
問題なさそうですね。

さて処理を書いていきましょう。
```
string $allJoints[] = `ls -type "joint"`;
string $jnt = "jnt_";
rename $allJoints[0] ($jnt + $allJoints[0]);
rename $allJoints[1] ($jnt + $allJoints[1]);
rename $allJoints[2] ($jnt + $allJoints[2]);
...(つづく)
```
このような書き方で問題なさそうですね。

...ちょっと待ってください。この書き方だとジョイントの数だけ`rename`コマンドを書かなければいけません。
数百回も書くのはちょっとスマートではないですね。

実はこのような場合のためにMELには**for文**というものが用意されています。

# まず文ってなに？
**文**の正確な意味や定義を知ることにあまり旨味はありません(プログラマーを目指す等で無いのならば)
ざっくりとした認識で、**そういう特殊な文法がある**という認識で問題ないです。

# for文
ある一定の処理を繰り返すための文です。


簡単な例で文法を確認していきましょう。
例題は「{"red", "green", "blue"}という配列の要素をすべてprintする」とします。
```
string $colors[] = {"red", "green", "blue"};
for ($color in $colors) {
    print($color);
    print("\n");
}
```
見慣れない文法だらけですが、とりあえず実行してみましょう。
([第8回](https://qiita.com/Hum9183/items/9a1b4489a6d4e7a86092)でも登場しましたが、`"\n"`は改行を表現する文字列です)
![MEL_learning03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/438379f8-0714-1e40-c5bd-ad2ce8e2b3a4.png)
![MEL_learning04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/cbc9e855-7420-6b10-1dd8-c5205122974f.png)
問題さなそうですね。

# for文の文法と挙動
文法は下記です。
```
for ($要素 in 配列) {
    処理;
    処理;
    ...(つづく)
}
```
**文**というくらいなので、基本的にはこの形以外は認められていません。

文法はこれに合わせて書いてくださいとしか言いようがないため、挙動の解説を行います。

【配列】
文字通り配列型の値を入れてください。
`ls`コマンドが返した値でもいいですし、colorsのように自前で用意しても問題ありません。
そしてこの配列の要素数(長さ)が**処理を繰り返す回数**になります。例えば、colorsを入れれば3回繰り返します。
また繰り返すことを**ループ**と呼称することがあります。

【要素】
ループを繰り返すごとに、配列の要素が1つずつ代入される変数になります。
colorsで言えば1周目は$colorには"red"が入っています。2週目は"green"、3周目は"blue"が入るという感じです。
また、この変数は型を宣言することができません。型はコンピュータが推測してくれます。

【処理】
通常通りMELを記述してください。基本的にどれだけ書いても問題ありません。
必ず守らなければいけないルールとしては、`{}`で囲まれた中でのみ記述しなければないないという点です。
また一般的に`{}`内の記述は**インデント**を行うという慣習があります。
インデントは一般的に「半角space4つ」もしくは「タブ」で表現します。
特に理由がない場合は、必ずインデントを行うことをおすすめします。

さて、以上を踏まえた上で、for文の挙動を正確に理解するために
```
string $colors[] = {"red", "green", "blue"};
for ($color in $colors) {
    print($color);
    print("\n");
}
```
上記のfor文をfor文を使わずに表現するとどうなるかを見ていきます。
```
string $colors[] = {"red", "green", "blue"};
string $color;

$color = $colors[0];
print($color);
print("\n");

$color = $colors[1];
print($color);
print("\n");

$color = $colors[2];
print($color);
print("\n");
```
いかがでしょうか？
for文のループごとに「要素」に配列の要素が代入されているイメージです。

最初のうちは完全に理解できなくても問題はありません。
この文法で記述すれば、**繰り返しの処理が行える**ということだけ理解できれば御の字です。

# 練習問題をfor文で書く
本題に戻りましょう。
今回の練習問題は「シーン内のすべてのジョイントの名前を、prefixが"jnt_"の名前にリネームするMEL」でした。

途中まで書いたMELは下記です。
```
string $allJoints[] = `ls -type "joint"`;
string $jnt = "jnt_";
rename $allJoints[0] ($jnt + $allJoints[0]);
rename $allJoints[1] ($jnt + $allJoints[1]);
rename $allJoints[2] ($jnt + $allJoints[2]);
...(つづく)
```
ジョイントの総数はその時々で変化するため決めることができませんが、for文をうまく使えば処理としてきれいに落とし込めそうです。
```
string $allJoints[] = `ls -type "joint"`;
string $jnt = "jnt_";
for ($joint in $allJoints) {
    rename $joint ($jnt + $joint);
}
```
こんな感じでどうでしょうか？
ジョイントをいくつか配置して実行してみましょう。
![MEL_learning05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/368edc6a-7b88-7bae-cfba-fbc6a1f47f6d.png)
![MEL_learning06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/99f1fded-ddd8-635f-97ac-bc8f232a17e3.png)
![MEL_learning07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121056/d92199a7-160f-0596-f285-57e905b4337b.png)
うまくいきましたね！

# 【もっと知りたい】for文とfor-in文
実は今説明したfor文は正確には**for-in文**といいます。
本家のfor文と比べると比較的平易で簡単に扱えるものになります。
凝ったことをしない限りはfor-in文で十分にMELを書くことができます。

もし凝ったことをしたい場合や、誰かのMELを読む際にfor文が出てきても読めるようにしたい！という方向けにfor文の紹介もしておきます。
```
string $allJoints[] = `ls -type "joint"`;
string $jnt = "jnt_";
int $length = size($allJoints);
for ($i = 0; $i < $length; $i++) {
    rename $allJoints[$i] ($jnt + $allJoints[$i]);
}
```
文法は下記です。
```
for (カウンタ変数初期化; 繰り返し条件; カウンタ変数の更新) {
    処理;
}
```
まず`i`は**カウンタ変数**といいます。
基本的にループごとにカウンタ変数の中身を変化させていき、ループごとの処理に差を出します。
変数名が`i`である必要はありませんが、`i`と命名するのが慣習となっています。

forの`()`内に3つのまとまりがあります。一つずつ見ていきましょう。

【カウンタ変数初期化】 
読んで字のごとくそのままです。カウンタ変数`i`を定義しています。型を定義することはできません。

【繰り返し条件】
この条件が満たされ続ける限りループが続くというものになります。
事前知識として`size`を知る必要があります。`size`コマンドは配列の要素数を取得するコマンドです。for文にはよく使われます。
例文では「&i < &length;」となっています。そのまま文章に翻訳すると「**カウンタ変数`i`が$allJointsの要素数よりも小さいなら**」という感じでしょうか。

【カウンタ変数の更新】
カウンタ変数が変更されない場合は、繰り返し条件を満たし続けるため、無限ループになってしまいます。
そうならないために、一回のループが終わるごとにカウンタ変数に変更を加えます。
`++`というのは**インクリメント**といい、**足す1**という意味です。
今回は使用していませんが`--`というものもあり**デクリメント**といいます。**引く1**という意味です。

【処理】
for-in文と同一です。

for-in文と比べるとかなりややこしいことがわかったかと思います。
そもそもシンプルで簡単なfor-in文ではなく、わざわざfor文を使うことなんてあるのかと思うかもしれません。
しかし用途は意外とあり、たとえば「奇数回目のループのときのみ処理をする」という場合や「3ループ目からスタートする」といったこと実現するときにはfor文でないと対応ができません。

# おわりに
いかがでしたか？
for文は最初こそとっつきにくいですが、慣れてくれば繰り返しの処理には欠かせない頼もしい存在になります。
イマイチわからなくても文法に合わせて使い続けることで、ある日いきなり理解できたりします。
最初は焦らず、便利な道具というような程度で使ってみてください。

次回はこちら→[【ArtistでもMELが書きたい】Maya MEL入門(11)](https://qiita.com/Hum9183/items/be10b53a6830fb820677)