
    クロージャをサポートした新言語 Closure Basicの作成


クロージャをサポートした新言語のコンパイラをデカルト
言語で作成します。 

コンパイルした結果は、中間コードに出力しデカルト言語
で書かれたVM(バーチャル・マシン)で実行することとします。 

基本的な手続き的な処理については、懐かしいBasicの文法
を使い、関数・サブルーチンの呼び出し処理と変数のスコープ
の管理をクロージャ対応としましょう。 

データの型は、浮動小数点数、文字列、クロージャの３つの
型を使います。 

関数クロージャは、ファーストクラス・オブジェクトとして
扱われ、変数に自由に代入でき、参照できるようにします。 


1. Closure Basicの実行方法

Closure Basicは、最新のデカルト言語の0.21.1バージョン
から実行できます。それよりも古いバージョンでは動作しな
いので語注意ください。 

さて、以下のように実行します。 

  descartes ClosureBasic プログラム


ファイルClosureBasicは、デカルト言語のパッケージの中
の"example/ClosureBasic/ClosureBasic"にあります。 

また、プログラムの例題が、パッケージの
"example/ClosureBasic/test"ディレクトリの下にあります。 

試しに、その中の一つfactor.cbsを実行してみましょう。 

このプログラムfactor.cbsは、以下のような関数の定義と
実行を行います。詳細については後の項で説明します。 

fact = {fun (n)
        if n <= 1 then
                return 1
        else
                return n*fact(n-1)
        end
}

print fact(3)
print fact(5)
print fact(8)


実行結果を示します。 

$ descartes ClosureBasic factor.cbs
Compiling...
Run
6
120
40320
result --
        <compile_run>
-- true

2. Closure Basicのプログラム例¶
まずは、Closure Basicのプログラム例を見てもらい、
雰囲気を味わっていただきましょう。詳しい文法の
仕様については、後の項で説明します。 

関数・サブルーチン以外は、特別な文法はないので
見ただけでほとんど理解できることでしょう。 

a. forループ¶
まず、for文によるループ処理です。 

Basic言語のfor, nextと同様の記述ができます。 




for i=0 to 10
        for j = 0 to 10
                print i,j
        next
next


変数は定義しなくても、プログラム上で現れたときから、
値を代入してそのまま使えます。 

上では、変数i, jに数字を入れて２重ループを実行して、
その値を表示します。 

このプログラムは、デカルト言語のパッケージの中の
"example/ClosureBasic/test/for2.cbs"にあります。
このページで紹介するプログラム例は、デカルト言語の
パッケージの中の"example/ClosureBasic/test/"にある
ので探してみてください。 

実行結果を以下に示しましょう。 

$ descartes ClosureBasic for2.cbs
Compiling...
Run
0 0
0 1
0 2
0 3
0 4
0 5
0 6
0 7
0 8
0 9
0 10
1 0
1 1
1 2

  ...途中略

9 8
9 9
9 10
10 0
10 1
10 2
10 3
10 4
10 5
10 6
10 7
10 8
10 9
10 10
result --
        <compile_run>
-- true


b. 配列
配列は、変数ですが特別扱いであり、その大きさを事前
に定義しておかなければなりません。 

dimを使って定義します。これもBasicから構文を借り
ています。 

dim2.cbsを以下のようにします。 


dim dt[10]
dim dt2[10]

for i = 0 to 10
        dt[i] = i
        print i, dt[i]
next

print dt[0]
print dt[9]
print dt[10]

for i = 0 to 10
        dt2[i] = i
        print i, dt2[i]
next

print dt[10]

print "--"

for i = 0 to 10
        print dt[i]
next


実行結果を以下に示しましょう。 


$ descartes ClosureBasic dim2.cbs
Compiling...
Run
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 10
0
9
10
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 10
10
--
0
1
2
3
4
5
6
7
8
9
10
result --
        <compile_run>
-- true


c. 階乗¶
リカーシブ(再帰的)な関数を書いてみましょう。階乗を
計算する関数を定義します。 

関数は{fun(引数) 処理本体}の形式の無名関数(かつ
クロージャ)として定義して、それを変数に代入する
と名前付きの関数になります。 

変数には決まった型がなく、数、文字列に加えて、
関数(クロージャ)を自由に代入することができます。 

これは、Basicにはない構文ですね。 

factor.cbsを以下のようにします。 

fact = {fun (n)
        if n <= 1 then
                return 1
        else
                return n*fact(n-1)
        end
}

print fact(3)
print fact(5)
print fact(8)

実行結果を以下に示しましょう。 

$ descartes ClosureBasic factor.cbs
Compiling...
Run
6
120
40320
result --
        <compile_run>
-- true


d. カウンター¶
クロージャを使ったカウンターのプログラムです。 

counter変数に関数のクロージャを代入しています。
このクロージャーの中では、カウンターの値を保持
する変数Cをローカル変数として持ちます。 

また、counter関数(クロージャ)の内部関数として、
以下のようなカウンター値を操作する関数を定義
しています。 

- カウンター値を表示するpr() 

- カウンター値を+1するinc() 

- カウンター値を-1するdel() 

- カウンター値に値を設定するset(ｎ) 

これらの関数内関数(クロージャ内クロージャ)の
pr, inc, del, setの変数にプログラムの最初で0を
入れているのに着目してください。なぜ、このよう
なことをしているかというと、これらの定義をcounter
関数の外で行っていないと、 pr, inc, del, setが
counter関数はローカル変数となってしまいcounter
関数の外部からアクセスできなくなるためです。
最初にグローバルな変数として定義しておいて、
counter関数内で関数として定義することにより、
counter関数のローカル変数にアクセスできるオブ
ジェクト指向のメソッド関数のように定義できるのです。 

なお、関数は必ず値を返すのですが、その関数の返り値
を使わないでサブルーチンのように呼び出すには、call
を使います。 

counter.cbsを以下のようにします。 

pr  = 0
inc = 0
dec = 0
set = 0

counter = { fun()
        c = 0

        pr = {fun () print c:return c}

        inc = {fun () c = c + 1: call pr(): return c}

        dec = {fun ()
                c = c - 1
                call pr()
                return c
        }

        set = {fun(n)
                c = n
                call pr()
                return c
        }

        return 0
}


print "call counter()"
call counter()

print "call pr()"
call pr()

print "call inc()"
call inc()

print "call inc()"
call inc()

print "call inc()"
call inc()

print "call dec()"
call dec()


print "call counter()"
call counter()

print "call pr()"
call pr()

print "call inc()"
call inc()

print "call set(7)"
call set(7)

print "call inc()"
call inc()


実行結果を以下に示しましょう。 




$ descartes ClosureBasic counter.cbs
Compiling...
Run
call counter()
call pr()
0
call inc()
1
call inc()
2
call inc()
3
call del()
2
call counter()
call pr()
0
call inc()
1
call set(7)
7
call inc()
8
result --
        <compile_run>
-- true


e. Y コンビネーター¶
Closure Basicの無名関数はまさにλ関数の形をしてい
ます。これを使って、Y コンビネーターを実現してみ
ましょう。 

Yコンビネータ(Y Combinator)とは何でしょうか。簡単
に言い切ってしまうと、無名関数だけで再帰プログラム
に相当する処理を可能にするものです。 

ここでは詳しくは述べませんが、WWW上でもいろいろと
書いて解説してあるのが検索すると見つかるので調べて
みてください。 

まず、ソースです。このソース内では、Yコンビネータを
使って、階乗とフィボナッチ数の計算を行っています。 

' Y combinator
'

Y =
{fun(f)
	g = f
	return {fun(p) 
		q = p
		return g(
			{fun(a) return q(q) (a)}
		)
	}
	({fun(p) 
		q = p
		return g( 
			{fun(a) return q(q) (a)}
		)
	})
}


' factorial function

print "Factorial from 1 to 10"
print


fact = {fun(f)
	g=f
	return {fun(n)
		if n <= 1 then
			return 1
		else
			return n*g(n-1)
		end
	}
}

for i = 1 to 10
	print Y(fact)(i)
next

print "--"
print "Fibonacci number from 0 to 10"
print

' Fibonacci number

fib = {fun(f)
	g = f
	return {fun(n)
		if n = 0 then
			return 0
		else if n = 1 then
			return 1
		else 
			return g(n-1)+g(n-2)
		end
	}
}

for i = 0 to 10
	print Y(fib)(i)
next




y.cbsというファイル名で保存します。 

実行結果を以下に示しましょう。 


$ descartes ClosureBasic y.cbs
Compiling...
Run
Factorial from 1 to 10 is displayed.

1
2
6
24
120
720
5040
40320
362880
3.6288e+06
--
Fibonacci number from 0 to 10 is displayed.

0
1
1
2
3
5
8
13
21
34
55
result --
        <compile_run>
-- true



