前回、前々回とuLispのグラフィクスの使い方を学び始めました。グラフィクスににはアリガチな(再帰駆使した)デモ例題有。マンデルブロ集合、バンズリーのシダなど別シリーズでやったものも在。デモのuLispコードを読んでいくと流石、素人老人が思いつかないスマートなコード。今回はホフスタッターQ数列でクロージャを学びます。
※Lispと一緒 投稿順 index はこちら
※実機確認は Wio Terminal で行ってます。
※使用させていただいとります uLisp のバージョンは 4.6b (Arm用)です。
Hofstadter Q sequence
ホフスタッターQ数列は、泣く子も黙る?『ゲーデル、エッシャー、バッハ』に登場するシーケンスであるらしいです。お惚け老人は読んでません。だってムツカシそうなのよ。しかし、その御本のお名前が心に残っているのは古いお友達(割と若くして亡くなってしまった)が、翻訳版の御本が登場した1980年代に熱心に読んでいたのを知っているからです。今度読んでみるか?お惚け老人には過ぎたるご本じゃろな?
さてこの数列に関してお惚け老人がどうこう言ってもせん無いので、いつもの通り説明はGoogleの生成AI、Gemini様にお願しています。
ほーん。ホントか?
uLisp上での Hofstadter Q sequence
この数列の出だしのあたりをuLisp処理系上でグラフにして表示するデモが以下にあります(以下ページにソースあり。)
Plotting the Hofstadter Q sequence
そのコードを読んでいて、Lisp素人老人は、ただならぬコードに気づきましたぞ。以下部分です。
ホフスタッターQ数列の定義そのものは、素直な再帰で分かりやすいのですが、その speedup 関数がテクです。どうもクロージャー駆使して再帰関数の計算時間を短縮しているみたい(毎回最初から計算しないでもOKよってことかい?)
これまた Googleの生成AI、Gemini様にLispにおけるクロージャの説明をお願いいたしましょう。
なお、Gemini様が上記でご提示されているコードは、Common Lisp上では動作すると思いますが、uLisp上ではエラーになります。過去回でやってますが、uLispは「厳格」なので未定義の変数に setq を許さないためです。以下のように defvar にしてやれば上記の例も動きます(Errorの行はsetqでやったところ。)
上記は、クロージャをdefvarしているので分かりやすいですが、元のホフスタッターQ数列のコードでは、defunしている関数 q を引数にするスピードアップ関数 speedup をクロージャーにして setq で q自身 に代入しているみたい。およよ。Lisp 素人老人には高度するぎる技だよ。まあ『ゲーデル、エッシャー、バッハ』に登場する数例だからこのくらい何てことない?
グラフ化
上記のデモ自体は、LILYGO T-Diplay RP2040という(手元にボードなし)というTFTディスプレイ付きの機種向けです。パネルのサイズは240×135ピクセルです。
当方手元にあるのは、Wio-Terminal LCDは360×240ピクセルの機種なので「ちょっと調整してやれば」表示はなんとかなるんでないの、という軽いノリでやってみました。結論からいうと
-
- qplot関数の修正の必要なし。qplotは表示画面の横サイズ、縦サイズを引数にとる。
- オリジナルのLILYGO用の (qplot 240 135) とすると動作はするが、描画結果の末尾の方が予期しない感じとなる。座標指定がオカシイ?
- Wio-Terminalの画面サイズで (qplot 360 240) とすると問題なく動作する。ただし、グラフの横軸目盛りが端になりすぎて見ずらい。
- テキトーに (qplot 355 235) と画面サイズを小さめにしたら目盛りも読めるようになった。
ホフスタッターQ数列(最初の方だけだけれども)のグラフの様子が以下に。
確かに予測できん動きをしている感じ。大丈夫か?