再帰処理

スクリプトを他人に説明する時、一番説明しにくいのは、再帰処理です。「自分自身を呼び出す処理」とか言っても、即座には何のことやらイメージできないですもんネ。

 

前回記事のレイヤーをファイルとして書き出す処理も、「もしレイヤーがレイヤーフォルダだったら、レイヤーフォルダの中身に潜って、同じ処理を繰り返す」再帰関数を作って処理していましたが、スクリプト文の引用を避けたのは、再帰関数の説明が長くなりそうだったからです。

 

レイヤーフォルダは、Photoshopの内部では「LayerSet」と呼ばれています。typename(当該項目がどんな種別かを判別する文字列)を調べると、普通の画像レイヤー(背景レイヤーやテキストレイヤーも含む)は「ArtLayer」、レイヤーフォルダは「LayerSet」という文字列が返ります。もし処理するレイヤーのtypenameが「LayerSet」だったら、中身に潜って処理を続行しないと、1階層しか処理できないポンコツスクリプトになってしまいます。

 

かと言って、前回のニーズだと、「レイヤーフォルダ階層の底まで潜ると潜り過ぎ」という条件がありましたので、「セルごとの連番レイヤーフォルダなら潜る。そうでない場合は潜らない。」というIF分岐を設けました。IF分岐はレイヤーフォルダ名で判別しました。

 

 

 

再帰処理は、私がスクリプトを覚え始めた20年前の初心の頃に、ウンウン唸りながら理解を進めました。まあ、普通の日常に、再帰処理なんて意識することはないので、もしかしたら「オブジェクト指向」よりも解りにくいかも知れません。オブジェクト指向的アプローチは、日々の料理のバリエーション展開など、身の回りに豊富ですが、再帰ルーチンは日常ではイメージしにくいです。

 

例えば、レイヤーフォルダに含まれるアートレイヤーの数を数えるスクリプトを考えた場合、レイヤーフォルダの階層が3階層までだったら、以下のように3回「for」ループで中身を総当たりで潜って処理できます‥‥が、まあ、こういうやり方は逆に大変です。

 

var artLayerCount=0;
for (var i=0;i<app.activeDocument.layers.length;i++){//第1階層
    if(app.activeDocument.layers[i].typename=="LayerSet"){
        for (var ii=0;ii<app.activeDocument.layers[i].layers.length;ii++){//第2階層
            if(app.activeDocument.layers[i].layers[ii].typename=="LayerSet"){
                for (var iii=0;iii<app.activeDocument.layers[i].layers[ii].layers.length;iii++){//第3階層〜これ以上潜らない
                    if(app.activeDocument.layers[i].layers[ii].layers[iii].typename=="ArtLayer"){artLayerCount++;}
                }
            }else{
                if(app.activeDocument.layers[i].layers[ii].typename=="ArtLayer"){artLayerCount++;}
            }
        }
    }else{
        if(app.activeDocument.layers[i].typename=="ArtLayer"){artLayerCount++;}
    }

}
alert(artLayerCount);

 

 

最大3階層までのレイヤーフォルダの中身のアートレイヤーをカウントして‥‥

 

 

‥‥と、一応は正確なレイヤー数はカウントしてくれます。

 

しかし、この3階層分の入れ子を直に文で書いてしまうと、正直、「今、何階層目か」、文を書いてて解らなくなります。非常に煩わしくて面倒です。ループを回す「i」変数を ii iii と切り替えるくだりは、煩雑そのもので書いてて混乱します。

 

書くのが面倒で読みにくい深い構造で、メンテナンス性(後で書き換えるなど)が最悪なのに、第4階層以下は潜ってくれず、処理してくれません。大変なわりに機能がショボい。

 

第4階層以上は潜れないので、

 

 

‥‥のような深い階層を持つレイヤー構造に対応できず、あくまで第3階層までしかカウントできません。

 

 

 

なので、再帰関数の出番。

 

var artLayerCount=0;
alert(artLayerCounter(app.activeDocument.layers,artLayerCount));

 

function artLayerCounter(_layers,_count){
    for (var i=0;i<_layers.length;i++){
        if(_layers[i].typename=="LayerSet"){
            _count=
artLayerCounter(_layers[i].layers,_count);//自分自身を呼びだす
        }else{
            if(_layers[i].typename=="ArtLayer"){_count++;}
        }
    }
    return _count;
}

 

 

「artLayerCounter()」という関数の中で、処理対象のレイヤーの種別がレイヤーフォルダだった場合は、関数自身にレイヤーフォルダの中身を入れ子で投げ込んでいます。

 

この構造ならば、階層が10階層だろうが、延々と中に潜って処理します。

 

しかも、文字数も少なく、見た目の階層は1階層なので、メンテナンス性も高いです。

 

 

 

 

ちゃんと、テキストレイヤー、画像レイヤー、背景レイヤーの合計を正確にカウントできています。

 

 

 

再帰処理を使わないと、文が長く読みにくくなる上に、処理対象の階層が深くなると機能は限定されて、いいとこなし‥‥です。

 

私は、一回でも再帰的な入れ子の処理が出てきた時は、迷わず再帰処理を使います。「潜るのは一度きりで今回限り」とは限らない事例が多いからです。後日に、もっと階層を潜れるニーズが発生した時に、再帰処理であらかじめ組み立てておけば、簡単な手直しで対応できます。

 

Photoshopに限らず、After Effectsのプリコンポーズした入れ子にも使えますし、OSのフォルダ階層にも応用できます。再帰処理を使えば、どんなに深いフォルダのファイルでも総当たりで一覧を作成できますから、過去作品のディスクアーカイブの内容一覧にも使えますネ。

 

再帰処理は、プログラムを覚えて色々な自動処理のパワーを実感する中で、「これは人力では不可能だ」と特に感じるものです。

 

アニメ制作における様々な事務的な雑事は、まさに再帰的な処理、反復処理の連続ですから、コンピュータを「絵や映像を作る時だけ」でなく、「作る前と作った後」にも活用して、全体的な作業効率を高めましょう。

 

アニメの映像そのものを作るのに精魂を使い果たした後で、事務的な処理にさらに1〜2時間居残りで作業‥‥なんて、できるだけ回避したいですもんネ。

 

 



calendar

S M T W T F S
     12
3456789
10111213141516
17181920212223
2425262728  
<< February 2019 >>

selected entries

categories

archives

profile

search this site.

others

mobile

qrcode

powered

無料ブログ作成サービス JUGEM