再帰では, 基本的にどんどん成長方法も実行も常に同じ規則で続いていきます.
この事情は数値引数で繰り返しを作る場合も同じですね.
しかし,
第1回目の実行, あるいは第2回目の実行に限って何かをする/しない
というようなことも可能です. 今回はこの手筋を説明しましょう.
基本的な構文は次の形です:
a(X,Y):****a(****,A) a(****,B)
AとBはX,Yを含まない文字列だと思ってください.
第n回目の実行でYとして呼び出されるものをY[n]と書くことにすると,
Y[1]=B, Y[2]=A, Y[3]=A, Y[4]=A, Y[5]=A,…
となることが分かると思います. 再帰の変数の更新規則の部分にXやYを混ぜないのは
一見無駄なようですが, これで第1回目の実行だけ特別という状況を作り出せました!
一番代表的な利用方法は, 初項のみ不規則な成長速度を与えるという物です.
例えば
a(X,Y):XXa(sssYX,ss) a(r,)
というコードを見てください.
XがsssYずつ増えていくというコードですが, 最初のYが""(空文字列),
2番目以降のYが"ss"となっているために,
[0,3,8,13,18,23,…]
という数列が実現できています. 単純に[3,8,13,18,23,…]という数列を実現したい場合,
b(X):XXb(sssssX) b(sssr)
というコードを思いつく人がほとんどだと思いますが, この方法よりも
上に挙げたコードの方が短くかけています(なお渦の開始向きが少し異なります).
初項をsssrと見なすのではなく, 「sssの部分も付け加えた」と思った方が短く書けるのです!
なお,
a(X,Y):XXa(sYYX,ss) a(r,s)
というような書き方も出来ますね.
もちろん, 実行部にYを混ぜても不規則な初項が楽しめます.
a(X,Y):XXXXYa(sX,l) a(sssr,r) などと書いてみると, 最初だけ規則と外れた方向転換をする…という具合です.
「最初の2項だけ特別!」みたいなこともできます(あまり使ったことはないですが…). つまり
a(X,Y,Z):****a(**,Z,A) a(X,B,C)
とすると,
Y[1]=B, Y[2]=C, Y[3]=A, Y[4]=A, Y[5]=A,…
となることが分かると思います.
他の方法と複合した例を見てみましょう.
a(X,Y):YYa(sX,XrXrX) a(,srsl)
この「Y=XrXrX」という変数の利用の仕方は, 多変数による繰り返しの講で紹介したテクニックで,
2倍関数を使わずにXrXrXの2倍を作るというような方法です.
このように変数Yを利用した場合, 第n段階のXがYに反映されるのは第n+1段階になってからでした.
すると, 第1段階のYはある意味全く利用されずに余っている部分になります.
ここを特殊な初項として利用することができるのです.
位置合わせや向き合わせなどに利用する場合が多いでしょうか.
利用頻度は高くないですが, 常に色んなものの利用可能性を視野に入れておくとよいでしょう.
最後に, 「最初の2項だけ特別!」を実現するちょっと変わった方法を紹介しましょう.
次のようなコードを考えます.
a(X,Y):****a(**,YY) a(,l)
今までのようにYの中身を全く新しくしてしまうのではなく, 2倍関数を使っています.
普通だと, Yが等しい規則でどんどん大きくなる格好ですね. 実際Yの部分は
Y[1]=l, Y[2]=ll, Y[3]=llll, Y[4]=llllllll,…
とあくまでも「常に規則的に」変化します.
しかし, Herbertにおいてはllllは何もしないことと同じなので
Y[1]=l, Y[2]=ll, Y[3]="", Y[4]="",…
と第1項, 第2項だけ特別な何かをやっているように見えることになります.
もちろんYの初項に"r"を入れたり"sr"を入れたりしても, 同じように初項, 第2項を特別扱いできます.
「4回繰り返し=8回繰り返し=…」というものなら何でもいいですね. なお,
a(X,Y):****a(**,YYl) a(,)
などとすれば
Y[1]="", Y[2]=l, Y[3]=r, Y[4]=r,…
とやはり特別な第1,2項が実現されます.
使用例は少ないですが, 「最初の2回だけ方向転換が怪しい!」というときは
この方法を疑ってみるとよいと思います.