例えば、引数Fを受け取って、
`Fiを評価して返す、という関数を作ってみましょう。
いろいろ考えてみると、次の式でできそうだと判ってきます*。
* 判らなくても、全く問題はありません……むしろこの項の重要性がより増すでしょう。
``si`ki
この程度の簡単な関数なら、頭で少し考えればつくれます。 しかし、私はその「頭で考える」過程を説明できる自信はありません。 ほとんどの方も同様だと思われます。
さらに、例えば「引数をXYZの三つ受け取って、
```YZ`XY`ZXを返す」
などという関数を頭の中で思い描けるでしょうか。
少なくとも私は無理ですし、他の人ができたとしてもその説明を聞いて理解できるとは思えません。
このような複雑な関数を生成するための方法というものが存在します。 この項では、その方法を解説していきます。
この項で使用する(今後も使用します)、擬似関数の文法を説明します。 文法とは言っても、C++やJavaのそれのような、複雑なものではありません。 登場するものは、たった二つだけです。
最初に挙げた例を擬似関数で書いてみましょう。
引数Fを受け取って、
`Fiを評価して返す関数です。
^x`$xi
ちなみになぜ私がこれを擬似と呼んでいるかかというと、Unlambdaの文法上このような「関数」は存在しないからです。
Unlambdaの文法には、^xも$xも存在しません。
ですが、だからといってこの擬似関数はUnalmbdaとは関係ないと決め付けるのは早計です。
この擬似関数を、Unlambdaの関数に簡単に変換する方法があるのです。
少し脇道に逸れました、上の擬似関数の説明をしましょう。
^xは、擬似関数の始まりを表します。xの部分に入る文字は実は何でもよくて、
これがまあ、関数の名前といったところでしょうか。
Unlambdaでは関数名での呼び出しなんてできませんので、ただ人間が読みやすくなるだけですが。
文字xにはもう一つの意味があります。
擬似関数は、他のUnlambdaの関数の例に漏れず引数を一つ受け取ります。
その引数の名前が、$xになるのです。
以上の事を踏まえて、もう一度上の擬似関数を見てみましょう。
まず、^xとありますので、関数xが始まっていることがわかります。
そして、`$xiは、この関数の本体になります。
^xの引数$xにiを引数として与えて呼び出しているのが一目瞭然ですね。
もう一つ例を示しておきましょう。
引数をsに渡し、
その返り値にさらにiを渡す関数は次のようになります。
^f``s$fi
先ほども書きましたが、擬似関数はそのままではUnlambdaのプログラムとして使うことはできません。 ここでは、擬似関数をUnlambdaの関数に展開する方法を示します。
^f$f
まずは、一番簡単な形から考えてみます。
^f$fの関数は、引数$fをとり、
それをそのまま返します。これは、iと全く同じ動作です。
よって、^f$fはiに展開できます。
^fF
次に、引数にかかわらずある組み込み関数Fを返す、
という形について見てみましょう。
これは、まさしく`kFの定数関数と同じ動作です。
よって、^fFは`kF
に展開できます。
^f`XY
そして最後に、この形を見てみましょう。
話を簡単にするために、^fXと^fY
をUnlambdaの式に展開する方法は既に分かっているとします。
前二つの関数より少し複雑になりますが、一息に言葉で説明してしまいます。
要するに、^f`XYがやることは、
『引数$fを^fXに渡し、
引数$fを^fYに渡し、
そして後者の返り値を前者の返り値に引数として渡す』ことです。
さて、これはsの説明とほとんど同じです。
実は、^f`XYは、
``s^fX^fYに展開できるのです。
以上の三つの説明をまとめると、次のようになります。
擬似関数の中身を先頭から一文字ずつ見ていき、
$xがあったら、iに展開する$x以外の組み込み関数があったら、
その前に`kをつけて展開する`があったら、``sに展開するということになります。
では、試しに擬似関数を展開してみましょう。
^x`$xi
三度最初の例を使ってみます。
まず、最初に`があるので、``sに展開します。
次には、$xがあるので、iに展開します。
次に、iがあるので、
`kiに展開します。
結果、この擬似関数は、
``si`ki
に展開できます。見事に、このページの最初にあるものと一致しましたね。
ただ、この展開は繰り返しているとえらく長くなります。
iを4回ほど展開してみましょう。
i `ki ``s`kk`ki ``s``s`ks`ss`kk`kk``s`kk`ki ``s``s`ks``s``s`ks``s`kk`ks``s`ks`ks``s`kk`kk``s`kk`kk``s``s`ks``s`kk`kk``s`kk`ki
4回目の展開で、実に81倍の長さになります。 Unlambdaのプログラムがすごい事になりやすい理由の一つが、これです。
とりあえず、ひととおり全ての擬似関数は展開できるようになりました。 が、ある特定の条件下なら、さらに短く展開する方法が存在します。 そのような方法を、二つ説明します。
一つ目の短縮記法は、擬似関数^fが以下のような形式の時に可能です。
^fF
ただし、Fは$fを含まず、
また副作用のない任意のUnlambda式とします。
この条件さえ満たしていれば、Fの長さは問いません。
薄々お気づきでしょうが、この擬似関数は`kF
に展開可能です。Fの長さに関わらずこのように展開できるので、
プログラムの短縮に大いに役立ってくれます。
ここで、副作用という言葉が出てきました。
副作用というのは、例えば文字の出力などの、
『実行する順番が狂うと困るもの』と覚えておきましょう。
具体的にUnlambdaで該当するのは、
関数.x, r, @, |, ?x, eの呼び出し、
及び評価時にこれらの関数を呼び出す関数です
(d, cは該当しないのかな? いまいち確信がもてない……)。
見た事のない関数がいっぱいありますが、後ほど説明しますので気にしないで行きましょう。
しかし、これら副作用を持つ関数であっても短縮記法は使用可能です。
`d`kFのように、
d関数を利用すれば、Fの副作用は問題にならなくなります。
もう一つの短縮記法は、次のような擬似関数に適用可能です。
^f`F$f
例によって、Fは$fを含まず、
また評価時に副作用があってはいけないものとします。
この時、この擬似関数は単純にFに展開できます。
また、もしFに副作用があった場合、
`dFとすることで問題を回避できます。
ただ、d関数の連発はUnalmbda的にあまり美しくないかも知れません……。
擬似関数は、複数の引数をとることもできます。 その場合は、擬似関数を重ねて書きます。
例として、最初の方で挙げた「引数をXYZの三つ受け取って、
```YZ`XY`ZXを返す」関数を書いてみましょう。
^X^Y^Z```$Y$Z`$X$Y`$Z$X
と、このようになります。
この関数は、引数を3つとるので、呼び出しの際には`を3つつける必要があります。
sと同じですね。
このような擬似関数を展開するには、内側の擬似関数から一つずつ展開していきます。 それでは、展開していきましょう。
^X^Y``s``s``s`k$Yi``s`k$X`k$Y``si`k$X ↓ ^X``s``s`ks``s``s`ks``s``s`ksk`ki``s`k`s`k$Xk`k``si`k$X ↓ ``s``s`ks``s`k`s`ks``s`k`s``s`ks``s``s`ksk`ki``s``s`ks``s`kk``s`ksk`kk``s`kk``s`k`sik
……ごめんなさい、こんなに長くなるなんて思ってもみませんでした○| ̄|_
$X, $Y, $Zの実行時に副作用があるかもしれなかったので、あまり短縮記法が使えませんでしたが、
もし$x, $y, $zが実行時に副作用がない事が保障されているのなら、
もう少し短くなるかと思います。
なんか悔しいので、試してみます。
^X^Y``s``s$Y`k`$X$Y``si`k$X ^X``s``s`ks``ss``s`kk$X`k``si`k$X ``s``s`ks``s`k`s`ks``s`k`ss`s`kk``s`kk``s`k`sik
おお、見事に短くなりました。
これで、この章の冒頭で出た難問も見事解決できました。 擬似関数万々歳です。