今回は,情報系の学生向けの話です.
0を含める自然数を定義するときに,0とsuc関数を使う方法があります.
0は自然数である.
Nが自然数のとき,suc(N)も自然数である.
これによって,
0
suc(0)
suc(suc(0))
suc(suc(suc(0)))
...
が自然数になります.これらをアラビア数字で0, 1, 2, 3 ... のように表記すれば我々の良く知っている数になります.
これを使って足し算を定義することができて,
0+N は N
suc(N1)+N2 は N1 + suc(N2)
こうすることで,たとえば,
suc(suc(suc(0))) + suc(suc(0))
は
suc(suc(0)) + suc(suc(suc(0)))
suc(0) + suc(suc(suc(suc(0))))
0 + suc(suc(suc(suc(suc(0)))))
suc(suc(suc(suc(suc(0)))))
という計算ができます.最初の式をアラビア数字で表すと
3+2
で,最後の式は
5
なので,ちゃんと計算できています.
僕はPrologに感動した人間ですが,Prologではこんな計算をさせて遊ぶことができます(Prolog のもっとすごい面白さは,足し算を教えただけなのに,勝手に引き算をやってしまう,ところなんですが).
さてこれをビスケットで表現するにはどうしたら良いか,という話です.
実は,残念ながら今のビスケットでは動きませんが,こんな感じでやれば動くのではないかと思ってます.
まず,使う記号は
![](https://blogimg.goo.ne.jp/user_image/42/b4/4230378dd3d36e99f6e4dbba232dfef0.png)
の4つです.
ビスケットには絵の継承を定義するための「ゆきだるま」という機能があります.それを使って自然数Nを定義します.
![](https://blogimg.goo.ne.jp/user_image/01/43/1da5bd7d8ff402205cd29aa06ca75098.png)
ゆきだるまの左側は,0はNを継承するという意味です.右側は,N+1はNを継承するとなってます.ここで継承に×が付いているので,実際にはこのように定義してもビスケットでは動作しません.継承で循環を禁止しているからです.循環というのはゆきだるまの上と下にNがありますが,継承をたどったときに同じ絵が出てくることを禁止しているということです.継承するというのは,ピンと来ないかもしれませんが,ビスケットでは同じ動きをする,と言い換えてもよいです.つまり,0はNと同じ動きをする.N+1はNと同じ動きをする,です.
で,もしこの継承の循環を許したとすると,この継承は
![](https://blogimg.goo.ne.jp/user_image/0e/3e/b4bafa9db4db6ae01badb79791cf9f8a.png)
このような図の継承をやったことと等しくなります.
0はNと同じ動きをする
0+1はNと同じ動きをする
0+1+1はNと同じ動きをする
0+1+1+1はNと同じ動きをする
これがずっと続きます.
で,Nに対して何か動きをつくれば,それはすべての数が同じように動くということになります.
では早速,Nに対して足し算を定義してみます.
![](https://blogimg.goo.ne.jp/user_image/11/7a/30ae5a89bee8ff29a2451abe25c7137c.png)
一つ目のメガネは 0+N は N になる.
二つ目のメガネは N +1 + N は N + N +1 になる
というものです.
ここまで用意しておいて,
![](https://blogimg.goo.ne.jp/user_image/3e/01/d376dac744d20f2f30e8a9f44722a6a3.png)
を書き換えることができるでしょうか.
実は動きません.というのは,NをN+1で置き換えるときに,+1がNの隣の図形と重なってしまって,ぐちゃぐちゃになってしまうからです.図形が自動的に重ならないようにずれてくれることが必要なのですが,別の方法を考えます.
継承でスケールの違う絵を入れてみます.これも今のビスケットでは禁止されてますが,そういう拡張をしたとしたら,
![](https://blogimg.goo.ne.jp/user_image/79/80/2e1cea631dc5ae8984623f1e3c68c6cf.png)
のようになります.
これは
小さい N +1 は 大きいNと同じ動きをする
というものです.ここで大きなNの横幅に対して,小さいNが丁度半分,+1も同じように半分になっているのがミソです.なので,1を足すごとにスケールが1/2になっていきます.たとえば苦しいですがこんな感じ.
![](https://blogimg.goo.ne.jp/user_image/40/a2/b08daea28c37200831773e06c26a4717.png)
構造がN倍に複雑になる場合,スケールを1/Nにするとすることで,どんなに複雑な構造でも,同じ面積に収めることができるようになります.
画面の解像度が無限に細かいとすれば,どんな複雑な構造でも表現できるようになるので,ビスケットのデータ表現能力は一般的な言語の項と同じにまでなれます.
まだ,実装されていないものの話ですから,なんとでも言えるというか,「そういう能力を持つようにビスケットを拡張すれば,そういう能力を持つようになる」という当たり前の話をしているだけなんですけど.
ここでは,項と呼んでますけど,ポインタと言い換えてもよいかもしれません.普通の言語が複雑なデータ構造を簡単に扱える理由はポインタを使っているからです.ビスケットはポインタがない言語ですから,こんな大変なことをしなければならないわけです.
プログラミング言語はもっともっと自由なんだということを言いたくて思考実験をしてみました.
0を含める自然数を定義するときに,0とsuc関数を使う方法があります.
0は自然数である.
Nが自然数のとき,suc(N)も自然数である.
これによって,
0
suc(0)
suc(suc(0))
suc(suc(suc(0)))
...
が自然数になります.これらをアラビア数字で0, 1, 2, 3 ... のように表記すれば我々の良く知っている数になります.
これを使って足し算を定義することができて,
0+N は N
suc(N1)+N2 は N1 + suc(N2)
こうすることで,たとえば,
suc(suc(suc(0))) + suc(suc(0))
は
suc(suc(0)) + suc(suc(suc(0)))
suc(0) + suc(suc(suc(suc(0))))
0 + suc(suc(suc(suc(suc(0)))))
suc(suc(suc(suc(suc(0)))))
という計算ができます.最初の式をアラビア数字で表すと
3+2
で,最後の式は
5
なので,ちゃんと計算できています.
僕はPrologに感動した人間ですが,Prologではこんな計算をさせて遊ぶことができます(Prolog のもっとすごい面白さは,足し算を教えただけなのに,勝手に引き算をやってしまう,ところなんですが).
さてこれをビスケットで表現するにはどうしたら良いか,という話です.
実は,残念ながら今のビスケットでは動きませんが,こんな感じでやれば動くのではないかと思ってます.
まず,使う記号は
![](https://blogimg.goo.ne.jp/user_image/42/b4/4230378dd3d36e99f6e4dbba232dfef0.png)
の4つです.
ビスケットには絵の継承を定義するための「ゆきだるま」という機能があります.それを使って自然数Nを定義します.
![](https://blogimg.goo.ne.jp/user_image/01/43/1da5bd7d8ff402205cd29aa06ca75098.png)
ゆきだるまの左側は,0はNを継承するという意味です.右側は,N+1はNを継承するとなってます.ここで継承に×が付いているので,実際にはこのように定義してもビスケットでは動作しません.継承で循環を禁止しているからです.循環というのはゆきだるまの上と下にNがありますが,継承をたどったときに同じ絵が出てくることを禁止しているということです.継承するというのは,ピンと来ないかもしれませんが,ビスケットでは同じ動きをする,と言い換えてもよいです.つまり,0はNと同じ動きをする.N+1はNと同じ動きをする,です.
で,もしこの継承の循環を許したとすると,この継承は
![](https://blogimg.goo.ne.jp/user_image/0e/3e/b4bafa9db4db6ae01badb79791cf9f8a.png)
このような図の継承をやったことと等しくなります.
0はNと同じ動きをする
0+1はNと同じ動きをする
0+1+1はNと同じ動きをする
0+1+1+1はNと同じ動きをする
これがずっと続きます.
で,Nに対して何か動きをつくれば,それはすべての数が同じように動くということになります.
では早速,Nに対して足し算を定義してみます.
![](https://blogimg.goo.ne.jp/user_image/11/7a/30ae5a89bee8ff29a2451abe25c7137c.png)
一つ目のメガネは 0+N は N になる.
二つ目のメガネは N +1 + N は N + N +1 になる
というものです.
ここまで用意しておいて,
![](https://blogimg.goo.ne.jp/user_image/3e/01/d376dac744d20f2f30e8a9f44722a6a3.png)
を書き換えることができるでしょうか.
実は動きません.というのは,NをN+1で置き換えるときに,+1がNの隣の図形と重なってしまって,ぐちゃぐちゃになってしまうからです.図形が自動的に重ならないようにずれてくれることが必要なのですが,別の方法を考えます.
継承でスケールの違う絵を入れてみます.これも今のビスケットでは禁止されてますが,そういう拡張をしたとしたら,
![](https://blogimg.goo.ne.jp/user_image/79/80/2e1cea631dc5ae8984623f1e3c68c6cf.png)
のようになります.
これは
小さい N +1 は 大きいNと同じ動きをする
というものです.ここで大きなNの横幅に対して,小さいNが丁度半分,+1も同じように半分になっているのがミソです.なので,1を足すごとにスケールが1/2になっていきます.たとえば苦しいですがこんな感じ.
![](https://blogimg.goo.ne.jp/user_image/40/a2/b08daea28c37200831773e06c26a4717.png)
構造がN倍に複雑になる場合,スケールを1/Nにするとすることで,どんなに複雑な構造でも,同じ面積に収めることができるようになります.
画面の解像度が無限に細かいとすれば,どんな複雑な構造でも表現できるようになるので,ビスケットのデータ表現能力は一般的な言語の項と同じにまでなれます.
まだ,実装されていないものの話ですから,なんとでも言えるというか,「そういう能力を持つようにビスケットを拡張すれば,そういう能力を持つようになる」という当たり前の話をしているだけなんですけど.
ここでは,項と呼んでますけど,ポインタと言い換えてもよいかもしれません.普通の言語が複雑なデータ構造を簡単に扱える理由はポインタを使っているからです.ビスケットはポインタがない言語ですから,こんな大変なことをしなければならないわけです.
プログラミング言語はもっともっと自由なんだということを言いたくて思考実験をしてみました.