第8話 「再帰ック」

--------------------- 第8話 「再帰ック」 --------------------------

はじまり、はじまり~。

<しかし、今だに信じられないわ。このスクリプト。一体何が起こってるの?>
何が知りたい?

<全部!>
だ・か・らぁ・・・

<分かってますわよ。そうね。まずは、共通の名前の抽出から、お願い>
ああソレなら最後の3行だよ。

<最後の3行って、match関数のこと? コレ中身一行よ。>
うん。

<コレで、二つの文字列の中から、共通項目を抽出できるっていうの?>
うん。

<そ、そんな事が? だって、ループもないし、何処まで比較したかの変数もないわよ>
少しは勉強しているみたいだね。

<ん?? match関数の中にmatch関数がある。コレって・・・>
ようやく気がついたね。「再帰」だよ。

<アタシ初めて見た。階乗・ディレクトリ・ハノイの塔とかサンプル以外で使われてるの>
<しかも効果的だわ。何と言っても一行なんだから。>
確かに見たことないね。UWSCでのコンナ使い方。 と言っても基本に忠実なんだけど。

<コレ実際には、どんな動きなの?>
ソノ前にアンタならどう組むかな?

<アタシ、このスクリプトの動きが知りたいんだけど。>
せっかちだな。ソノ理解の為にも考えたら?

<そう? 今回だけだからね。アタシならループで一文字ずつ比較するかな>
<ソンでもって一致した文字数までの数値を返すかな。>
それでも悪くはないね。ただUWSCには一文字を抽出するMID関数はないし、
一文字ずつだと両方の文字列をいじるから面倒だね。

<そっか~じゃあどうするの?>
大きい方を固定して、もう片方の文字を追加するか、削っていって比較する。
似たのが多いから、まとめようとするので削ってい行く方が良いかな?

そうするとコレになる。「再帰」ではまず脱出条件を明確にする。コレが
if pos(s1, s2) = 1 or s1 ="" then result = s1 で空か共通文字列そのまま。
そうでなければ、末尾を一文字削ったモノで自分自身を呼び出す。

<なるほどぉ。片方だけを操作して、かつ、そのままの文字列でシンプル化かぁ。>
<それで再帰はループも使わず、処理できるんだ。まさに「再帰ック」ね>
オイッ! 寒すぎるぞ!


----------------------------------------------------------------------

提供は:「再帰ック」な変人。Linersでした。

----------------------------------------------------------------------

// 再帰を再考してみる。

再帰は最も基本的で、最も使われなくて、最も不思議な技法の一つだ。

関数定義の例には必ず出てくるのに、実際に使われている例は、ほとんど見たことがない。
良く例に出されるのは、階乗計算、ハノイの塔、ディレクトリ検索、フィボナッチ数列、などだが実際にはほとんど使われない。

階乗計算はほとんど使わないし、フィボナッチ数列は時間がかかり過ぎて実用性ないし、ハノイの塔のような問題は稀だし、
ディレクトリ検索はそのまんま使えばいい。

つまり、再帰の応用例が極端に少ない。にもかかわらず、基本の技法として必ずで出て来るのは、ある局面では絶大な威力を発揮するからだ。
良い例は、ハノイの塔を再帰を使わず記述するのが大変である事だ。

再帰は、プログラミング技法の「切り札」である。

私は、この再帰を良く使う。さすがに記述に困るほどの場面での使用は稀だが、簡単な記述には良く使う。
その目的は、コードが短くなるし、アルゴリズムの見通しがよくなるからだ。ただし、初心者向けとしては、理解し辛い部分があるので、あまり公開してしないだけである。

再帰には、処理が遅い、スタックオーバー、無限ループの危険性が高いなどの副作用もある。「諸刃の剣」でもある。

で、感心な実際の例なのだが、意外に会社のプログラムなんかに使っていてそのままでは晒せないが、掲示板の例で一つ上げてみよう。

[質問抜粋]
デスクトップに"tanaka"というフォルダがあり、この中に以下のPDFが入っているとして、
AA1122.pdf AA1145.pdf AA1155.pdf GG5678.pdf GG5987.pdf ZZ5678.pdf を AA11.pdf GG5.pdf ZZ5678.pdf
という様に名前が同じところまでまとめたいのですがどうすればよろしいでしょうか。

質問自体が相当厄介ですが、ココでは A1122.pdf AA1145.pdf を AA11 にする最大公約文字列?に再帰を使用します。考え方としては、末尾から一文字づつ削っていってマッチするかを調べます。
マッチするか文字列が空なら戻り、そうでなければ一文字少ない自分自身を呼び出します。

その効果は・・・関数内が一行ですから絶大な威力ですねぇ。(Linersらしいトンでもなさ?)

msgbox(match("AA1122.pdf", "AA1145.pdf"))

function match(s1, s2)  // 関数: 語尾を詰めた自分自身を呼び出す(再帰)
  if pos(s1, s2) = 1 or s1 ="" then result = s1 else result = match(copy(s1, 1, length(s1)-1), s2)
fend