計算言語5(帰納的関数)
このページはマス旅の一部です。
今回は機能関数を作ろう。
ざっくり、whileでプログラムがかける関数を「while関数」と呼びます。
while関数は今回紹介する「帰納的関数、機能関数」とつながります。
さて、帰納的といえば、数学的帰納法や反復計算や再帰関数を連想しますね。
想像ではなく、コトバで定義してみよう。
まず、帰納的関数のクラスには初期関数が3つあります。
ゼロ関数、後者関数、射影です。
この3つは帰納的です。
・ゼロ関数は変数を取らずただゼロを返します。zero()=0です。
・後者関数はsucc(x)=x+1,前回やったadd1(x)と同じですね。
・射影はu_i^n(x1,...,xn)=xiのように、n個の引数からi番目の成分を抜き出す。
これらは前回作った言語Mで実現できそうですね。
初期関数から開始して「次の3つの操作」を使ってできる関数は、帰納的関数となります。
「3つの操作」は、合成、原始帰納、最小だ。
・合成:m個の引数をとる関数fと、n個の引数をとる関数g1,g2,..gmが帰納的なら、f(g1(x1,...,xn),....,gm(x1,...,xn))も帰納的だ。
・原始帰納:n変数のgと(n+2)変数のhが帰納的なら、(n+1)変数のfも帰納的になる。
f(0,x1...xn)=g(x1...xn),f(k+1,x1...xn)=h(f(k,x1...xn),k,x1...xn)
特に、
n=0の場合、f(0)=g、f(y+1)=h(y,f(y))となるfが原始関数。
n=1の場合、
f(0,x)=g(x),f(k+1,x)=h(f(k,x),k,x)となるfが原始関数。
f(x,0)=g(x), f(x,k+1)=h(x,k,f(x,k))となるfが原始関数。
hはfの前のステップのkとfも受けるから、gよりも引数が2個多いことは注目しておこう、hの引数の位置は任意だけれど、それにfの引数の順序が一定であればよいですね。
・最小化:g(x1,...xn,y)=0となる最小の自然数をyを探す操作。
f(x1,....xn)=μ_y[g(x1,...,xn,y)=0]
この操作でできる関数fも帰納的だ。
ただし、この最小化操作で条件を満たすyが見つからない、停止しない無限ループになることもあるので、入力しても値が定義されない可能性を許した関数として、部分関数と呼ぶ。
以上の3初期関数に3操作を繰り返してできる(部分)関数のクラスを帰納的関数と呼ぶ。
天下り的な始まりなので、実例で確かめていこう。
<自然数は帰納的関数>
one=succ(zero())=succ(0)=1
two=succ(one)=succ(1)=2,
three=succ(two)=succ(2)=3,
....
1,2,3.,...という自然数が、帰納的関数になる。
ペアノ的で単純で美しいですね!
<加算、乗算は帰納的関数>
h(x1,x2,x3)=succ(u_3^3(x1,x2,x3))
3引数の第3成分(前の計算結果)を取り出して1加算するh関数を使うと、次のようにして加算plus(x,k)は原始帰納で定義できる。
plus(x,0)=u_1^1(x)
plus(x,k+1)=h(x,k,plus(x,k))=plus(x,k)+1
h'(x1,x2,x3)=plus(u_1^3(x1,x2,x3),u_3^3(x1,x2,x3))
h'は3引数の先頭と最後を取り出し加算する関数とすれば、
すると、次のようにして乗算mult(k,x)は原始帰納で定義できる。
mult(0,x)=zero(x)=0
mult(k+1,x)=h'(mult(k,x),k,x)=plus(mult(k,x),x)
これって、同じ数xのたし算の反復がかけ算になるという
常識的な定義をうまく表しているね。
<論理演算子も帰納的関数>
省略しますが、上と同様にして、原始帰納を使うと、
帰納関数をぞろぞろ作れる。
pred(0)=0, pred(k+1)=k
gt(x,y)=if(x>y,1,0)
zero?(x)=if(x==0,1,0)
dif(x,y)=if(x>=0,x-y,0)
abs(x,y)=plus(dif(x,y),dif(y,x))
eq(x,y)=if(x==y,1,0)
ge(x,y)=if(x>=y,1,0)
面白いことに、算術関数を流用して、論理関数が作れる。
1を真、0を偽とし、述語を{0,1}とする関数を定義する。
not(p)=zero?(p) これはpがゼロなら真で1、それ以外は0を返すからだ。
and(p,q)=mult(p,q)。
乗算は(p,q)=(1,1)のときだけ1になるから同じだとわかる。
andとnotが定義できれば、おなじみのドモルガンの定理でor関数も帰納的になるね。
自然数の上の(部分)関数fが 帰納的(部分)関数であることとwhile関数であることは同値だという。
車輪の開発ワールドになっているね。
でも、それはそれで、
基礎を振り返る、建物の地盤を調べるということで、大切なことではあるね。
課題:mult(3,4)を原始帰納を使って、12になることを確認しよう。
h'(x1,x2,x3)=plus(u_1^3(x1,x2,x3),u_3^3(x1,x2,x3))
mult(0,x)=zero(x)=0
mult(k+1,x)=h'(mult(k,x),k,x)=plus(mult(k,x),x)
mult(x,0)=zero(x)=0
mult(x,k+1)=h'(x,k,mult(x,k))=plus(x,mult(x,k))
これを前提とする。
<手動トレース>
4を0にするよりも、3を0にする方が回数が少ないから、
mult(3,4)
=plus(mult(2,4),4)
=plus(plus(mult(1,4),4),4)
=plus(plus(plus(mult(0,4),4),4),4)
=plus(plus(plus(0,4),4),4)
=plus(plus(4,4),4)
=plus(8,4)
=12
plus(x,4)
=plus(x,3)+1
=plus(x,2)+1+1
=plus(x,1)+1+1+1
=plus(x,0)+1+1+1+1
=x+1+1+1+1=x+4
となるから、
plus(x,4)はxから4後者の数。
x=0から始まって、3回入れ子plus(x,4)を繰り返すので、
mult(3,4)=((0+4)+4)+4=4+4+4=12
こうして、
乗算⇒加算⇒後者関数
と、基礎に還元できることがわかる。
<geogebraでコード化>
mult(3,4)を作る。
g(x)=x+4とすると、
Iteration(g,0,3)=((0+4)+4)+4=12となるはず。
xの初期値0でgを3回繰り返すから。
そこで、
xx=4
mxx(x)=x+xx
とすると、mxxはxのxxだけ後者を指す。
k=3
mul=Iteration(mxx,0,k)=Iteration(+4,0,3)=12
という数値になる。
「新規ツール」で、「入力」をxx,k、「出力」をmulとして、名前をMLとすると
ML(3,4)=12となるはず。
ML(5,8)=40となる。
GUIとしては、
p=InputBox(k)
q=InputBox(xx)
r=ML(k,xx)
text=p+"x"+q+"="+r
タイトルは「かけ算をたし算の反復によって計算しよう。」
これで、かけ算関数をたし算の反復で作ることができた。
もちろん、たし算関数も後者関数で作れるでしょうが、
このくらいにしておこう。