星田さんの記事に対するコメント。
>> 2022/12/25
Cametanさんからの課題「Typecaseを再現せよ」。正直全然分からなかったので素直に回答を拝見して勉強させていただきます(^_^;)
GaucheやRacketのmatch-lambdaの話でも書いたけど、たま〜にLisp系のリファレンスだと「マクロでの実装方法」がそれと知られずに載ってる事があるのね。
CLHSのtypecaseに書いてあるNotes:はこうだ。

これは要するに
(typecase test-key
{(type form*)}*)
と言うフォーマットを
(let ((#1=#:g0001 test-key))
(cond {((typep #1# 'type) form*)}*))
に変換する、って言ってる。
元のフォーマットをもうちょっとSchemeっぽく表現すると
(typecase test-key
((type form ...) ...)
って事だ。
変換先もちと見慣れない表現#1=#:g0001ってのが出てくるけど、これはSchemeでは無視して構わない。ANSI Common Lispだとフツーの変数を書けば捕捉されておかしな挙動になる為、gensymと言う決して環境では捕捉されない特殊なシンボル生成をしないといけないんだけど、Schemeではそこは心配する必要がないんで、xでもyでも何でも構わないんだ(これがSchemeのマクロが「衛生的マクロ」と言われる所以だ・※1)。
いずれにせよ、この変換先のフォーマットもSchemeっぽく書けば
(let ((foo test-key))
(cond ((typep foo 'type) form ...) ...))
を現していて、しかも変数捕捉がなく、test-keyがfooに束縛されている以上、実はletは意味がなくって、これは単に、
(cond ((typep test-key 'type) form ...) ...))
を意味してる。
つまり、
(typecase test-key
((type form ...) ...)
を
(cond ((typep test-key 'type) form ...) ...))
に変換しろ、って言ってるだけなんで、もうこうなればdefine-syntaxで書く基本は、
(define-syntax typecase
(syntax-rules ()
((_ test-key ((type form ...) ...))
(cond ((typep test-key 'type) form ...) ...))))
となって、ここからスタートして考えていけばいい、ってヒントになっている。
もちろん、Schemeにはtypepってのは無いんで、述語を書いてけばいい、って話になってくんだけど、いずれにせよ「リファレンスを参考にしながらマクロを書いていく雛形」としてはこれでいいと思う。出発点だな。
あとは、色々と帳尻を合わせるように作り上げていけばいい、と言う事になる。
そのままArc-ifもイジらせていただく。引数1つのときにはそれ、2つの時には2個目、3つ以上の時にも2個目なのか・・
2つとも成り立つ時には後が返され、2つとも偽なら3つ目以降で同じパターンの繰り返し・・・うーん、二択を優先順位をつけていくつも重ねて処理出来るということ・・かな?
arcのifは一回チュートリアル見てみればいいかも。
An if with more than three arguments is equivalent to a nested if.
(if a b c d e)
is equivalent to
(if a
b
(if c
d
e))
If you're used to languages with elseif, this pattern will be
familiar.
訳: 3つ以上の引数を持ったifは入れ子になったifと同じだ。elseifを持ったプログラミング言語に慣れてたらこのパターンはお馴染みだろう。
結局、arcのifはいわばLispのcondと同じなんだけど、暗黙のbeginがない。暗黙のbeginがないから忌まわしいカッコがない、ってだけなんだ。
まさしく、マッカーシーが「失敗した!」って言ってた部分が修正された、って事になる。

>> 22/12/24
Cametanさんの記事から・・ムズっ!
あー、ちとひねた書き方だったかしらん。
基本はこうなのね。

多分こっちの方が分かりやすい。
恐らく川合史朗さんだとこう書くかな・・・・・・。
なお、identityは何度か出てるけど「恒等関数」ってヤツで平たく言うと単なる(lambda (x) x)って関数。引数をそのまま返す。Racketのgroup-byがキーとして「データのどの部分を指標としてまとめるか」関数で指定するんだけど、この問題の場合は大枠が単なるリストなんで「どの部分」って言われてもそのまんまなんで、恒等関数で指定してます。
んで、僕の方はちと紫藤さんの影響がデカイ。こう、ラムダ式の中の(cons 何とか x)と言う「同じパターン」が2つあると、どうしても縮めたくなっちゃうのね(笑)。同じパターンを2つ書きたくない(笑)。
だからconsとxをifの外に括りだしたくなっちゃうわけ。

これらのバリエーションは色々と考えられるんじゃないの?とは思う。
例えばmatch-lambda*とmatch-letを使って次のように書いてみたり。

その辺は好みかなぁ。
しかもお題のPythonの方は予想を遥かに超えて大変でライブラリの話とも合わさってPythonに戻るのはまだ先にしようと思いました。
Pythonは色々とadhocな拡張が目立ってきてて、一貫性はちと崩れてきてるかなぁ。
意外とその辺、Rubyなんかの方がストレスは感じないかもしんない。あるいはJavaScriptとかか。
一方僕と言うと、ナウシカのページの表示の部分に迷宮の通路の複数のページを用意しておけば同じ形式と2次元配列でなんちゃって3Dダンジョン出来るな・・とかお気楽な事を妄想してました。
多分そのうち、ダンジョンRPGだろ(笑)。
と言うか、Ultima以降の2D RPGは違うんだけど、Wizardryみたいな3D RPGの方ってどう考えても偶発的に出てきたコンピュータサイエンスの落とし子なんだよなぁ・・・こっちの方が言っちゃえば実装はラクな方なんだと思う。
いつぞや書いたけど、そもそも3D RPGは大学でのメインフレームで出てきたブツで、そして重要なのは実装しやすいブツがまずは実装されるの法則なんだよな。
黎明期のメインフレームだと2Dだとローグライク、そして3DダンジョンRPGがまず実装された、ワケだ。ラクだから。そしてそれらはコンピュータサイエンス的な教育で得た知見が応用しやすかったわけだ。
だから、多分そのうち、ダンジョンRPGに挑戦する、って事になるとは思う(笑)。まず間違いなく(笑)。
個別の関数の働きとかは分かるんですけど(そして忘れたんですけど)、良くて複数組み合わせて返り値が得られる・・程度なんですよね。クラスも配列も辞書もぼんやりと(当時は)分かったけど 「で?」 となった時点で「じゃ次!」って感じなんですよね。
うん、分かる。
やっぱ何度も書くけど、そもそも「ブラウザ使って全部済まそう」ってのが無理ゲーなのよね。結局「練習問題」がキチンと作れない。
練習問題がキチンと作れない以上、そこで上達はしないわけでしょう。
星田さんが、色んな「ゲームっぽい」問題を検索して挑戦する、って事やってなけりゃ色々と難しかったんじゃねぇのかなぁ。
あと今でも笑うのは例のLisper養成ゲーム「りりかるLisp」ですねぇ。あれ、一応なんとか終わった時点で「予想よりもかなりスムーズに終わったようだ」みたいなコメントいただきましたけど
「いや、()とシンボルの配置を全パターン総当たりのノリで変化させてただけで全然理解してないんですけど(^_^;)」
とか思ってました。
いや全然。
っつーか「()とシンボルの配置を全パターン総当たりのノリで変化させてただけ」ってのがフツーできないんだわ(笑)。そこができたのがスゴイ。
あとね、1年過ぎたし、リリカル☆Lispもう一回やってみてもいいかも。1年に一回やってみれば「すげぇ自分の進捗が分かる」から(笑)。
あれ、練習問題が10問前後しかないけど、割に珠玉の問題揃えてある、って思うのね。進歩確認するには丁度良いと思う。1年前「何だこれ?」って思ってた部分がスンナリ解けるとこもあるだろうし、そうじゃないとこもあるだろうし。
確認するには凄くコンパクトで良い問題揃ってる、ってのは間違いないです。
何回も書きますけどけど末尾再帰って
(define (fun x y z)
...
(fun x y (cdr z))
↑ココ、一番ケツで再帰させることだと長らく思ってましたからね。
うん、僕も最初、末尾再帰って全く意味が分からんかったわ(笑)。ANSI Common Lisp最初やった時出てこなかったしなぁ。
んで、C言語やってる人たちの殆どが知らんと思う。実は末尾再帰の意味を掴めてる人は少数派なんだ。
もう星田さんはその少数派に入ってるんだよ。
あ、で本題なんですが。例の「あの女性キャラ」なんですけどどうも他の言語も全部担当してるみたいなんですよ。動画を見てる限りだと文字入力とおしゃべりのタイミングというか呼吸がシンクロしすぎてるので、ひょっとして凄い人なのでは?とか思ってたんですが。
あ〜。
でも単純に、誰か声優さんなんじゃないの?とか思ってた。
打ちながら解説してるにしてはヘンなんだよな。早口になってるし。
ただ、難しかったら今は理解を避けていい。
と言うのも、SchemeのマクロはANSI Common Lispのマクロに比べると自由度が低いが、逆に言うと「初心者に優しい」のは機構的に変数捕捉を「絶対行わない」のがSchemeのマクロだからだ。
ANSI Common Lispのマクロで、まず一番最初にマクロ初心者が戸惑うのがこの「変数捕捉」の問題で、一体いつ変数捕捉が起きるのかイマイチ良く分からん事に由来する。
Schemeではこの辺の事を気にしなくて良いんで、だからこそ、syntax-rulesは初心者に比較的優しいんだ。