見出し画像

Retro-gaming and so on

RE: プログラミング学習日記 2023/02/09〜

星田さんの記事に対するコメント。

> 2023/02/13〜15 

 えっ!そうだったのか・・という事はCLをやれば自然とオブジェクト指向の勉強にもなるわけか・・

ん〜、必ずしもそうじゃないとは思いますが。
あくまで「データ型の設計」がそうだ、って話であって、ANSI Common Lispのオブジェクト指向(通称、CLOS(Common Lisp Object System)と呼ぶ)はまた独特なんで、ある種混乱するかもしんない(笑)。

いずれにせよ、Common Lisp(ANSI規格になる前)やMacLisp(MITで開発されたCommon Lispの前身・・・Emacs Lispの先祖)ではCG(Computer Graphics)の研究がさかんに行われていて、そのCGをやる為の武器がオブジェクト指向だったのね。
従って、「良い」オブジェクト指向を実装する必要性があったわけ。それでCG絡みでオブジェクト指向が研究されてた、って言う背景がある。
それで、その研究成果と共に、ANSI制定化する際に、ANSI Common Lispはオブジェクト指向言語として仕様が決まります。
なお、ANSI規格だと「一番最初のオブジェクト指向言語」になっていて、ANSI C++の制定より早い(C++のANSI標準化は1998年で、ANSI Common Lispの制定(1994年)より4年遅い)。
なお、このCommon LispやMacLisp上の研究成果の一つが、度々名前を出してるSymbolics社、と言うLispマシン(ワークステーション)に反映されていて、1990年代にはCGマシンとして知名度が高かったSillicon Graphics(Nintendo 64の開発協力社であり、The AbyssTerminator 2Jurassic Park等のCGを担当したマシンの製造メーカー)のライバルメーカーとして戦っていくわけだ・・・両者共今や消えちゃったけどね(笑)。兵どもも夢の跡、だ(笑)。

なお、だ。
「オブジェクト指向」ってぶっちゃけ、正確な定義はないんだけど、大まかなコンテクストってのは大体2種類に分けられるのね。
一つはユーザーが言うトコの「クラスを備えていて、メソッドがあって、"継承"があって・・・」ってシステム。もちろんこれは「誤解に」基づいている一般的な定義なんだけど、まぁ、大方の人が言う「オブジェクト指向」ってのはこれを指す。
もう一つは、前回見たように、プログラミング言語の「仕様策定者」や「実装者」が言う「オブジェクト指向」。この場合は、たとえ一般的に言うクラスとか継承とか言う機能がなくても「データ型がヒエラルキーを持っている」事を指したりする場合が多いわけ。
紛らわしいんだけど、誰が発言してるのか、文脈は何なのか、って辺りで「オブジェクト指向」の意味合い、っつーか「使われ方」が違うんで、この辺は気をつける必要がある。
あんまJavaは詳しくないんだけど、例えばJavaで言う「オブジェクト指向」は前者の意味なんだけど、後者の意味は持ってないんじゃなかったかしらん。言い換えると、Javaは「完全なオブジェクト指向ではない」筈。知らんけど。そういう場合は「完全なオブジェクト指向ではない」とか言う言い方したりするんだよな。ホンマ紛らわしい(笑)。
いずれにせよ、ユーザーが言う「意味」と仕様策定者や実装者が言う「意味」はしばしば食い違ってる。ユーザーが言う場合、「このシステムはクラスがない」とか「継承がない」とか「カプセル化出来ない」と言う「機能面」で、「これは完全なオブジェクト指向だ」「あれは不完全なオブジェクト指向だ」とか言うんだけど、仕様策定者とか実装者側は割に、そう言った「機能面」で言う事は少なくって、「データ型がキチンとしたヒエラルキーを持って設計されているか否か」で、「完全」とか「不完全な」オブジェクト指向、って言い方をしたりする。
紛らわしいんで、その辺ホント、「発言の文脈」を良く読んでください。

 しかしコレには驚いたなぁ・・これでエラーが出ないとなると相当しっかりと管理しないと謎の挙動で大変そう

そう、C言語では「危険なプログラムが書ける」ってのはその性質所以なんだよね。
そして実体はやっぱ「高級アセンブリ」なんだよ。高級言語とは言えない言語なんだよな。

これも何度か言うけど、色んな人にインタビューしてみたんだけど、結局良く言われるような

「C言語をマスターすればコンピュータの仕組みが分かる」

とか言うのってウソなんだよ(笑)。そんなヤツは一人もいない、って言って良いと思う。
事実は「コンピュータの仕組みを分かってる人が」C言語を使うのが上手い、って事。Cause & Effect(原因と結果)が逆なんだ。
もっと具体的に言うと、例えば「アセンブリ言語でのプログラミングに慣れてる人がC言語プログラミングを"ラクに感じる"」ってのが事実なんだよな。
C言語を学んでも「コンピュータの仕組みは分からない」、ってのをいい加減気づけばいいのに気づかないんだよな(笑)。あり得ない「学習効果」を期待して学生に無茶振りしてるわけ(笑)。
だったらむしろアセンブリをガリガリ書く訓練を最初にした方がマシとちゃうんか、とか思ってる(笑)。いまだに学習効果が怪しい事を散々言って、C言語を学生にやらせてる、ってのが実態なんだわ。

 mismatchを受け取るHandlerのオプション?って見つからないんだよなぁ・・まあ、使うアテもないですが



つまり、機能的には古いんで、現時点での推奨はraise-arguments-errorだ、と。
で、大体のケースだとexn:fail:contract?で掴まえられるんじゃないかしらん。

 ずっとcall/ccの/って?とは思ってたのですが、なんだwithって事か。
  call-with-current-continuitionをただ略しただけかw

そう、Schemeだと大体"/"はwithの意味。
実際は、殆どのScheme実装はcall/ccを持ってるけど、それは大体は、正式名称であるcall-with-curent-continuationへのエイリアスになっている。

なお、C言語なんかのAT&T文化圏だと、<stdio.h>とか、「一見何だか分かんない酷い略称」が多く、「短さこそ正義」なんだけど、反面、ANSI Common LispやSchemeなんかの「MIT文化圏」のプログラミング言語だと「クッソ長い関数名」でも全然ヘーキで付けるんだよな(笑)。
これは結局、MITって豪勢な開発環境を使ってプログラミングする、って歴史があるから、なの。言っちゃえばMITってのは「IDEを使ってプログラミングする」のが伝統で、「自動補完アリ」の環境だとどんな長い関数名をつけようと構わない、からなんだよな。
逆に、C言語が作られたAT&T文化圏だと、貧弱なテキストエディタ(それこそWindowsのメモ帳みたいな)でプログラミングする、ってのが前提になっていて、そういったわけで「タイピングがメンドい」んで、短い関数名なんかを付ける、って背景になってるわけだ。
MIT派とAT&T派、って言わばアメリカの二大派閥なんだけど、いずれにせよ「開発環境が何なのか」ってのがプログラミング言語設計に大きな影響を与える、って良い例だと思う。IDEが必要な言語には要IDEでプログラミングする、ってのが当たり前であって、良くいる「テキストエディタ原理主義者」・・・「プログラミングは素のテキストエディタでするべきだ」と言う主張は実は物事の一面しか見ていないしょーもない主張だ、って分かると思う。
「プログラミング言語自体が開発環境の影響を受けてる」のが実際なんだよ。

余談だけど、Rubyなんかでendだらけ、ってのも何故か、と言うと、IDE(っつーかEmacsか)で、endが自動補完出来る、って前提であれを決定した模様だ。
素のテキストエディタでendなんざ打ってられるか、って前提でああ言う言語デザインになってるわけ。

 ドキュメントの続きにはこの本へのリンクが。この際DeepLを使ってではあるけど、一通り読んでみようかなぁ・・

うん、これがSICPはダメ、って論文を書いた人達(主にPLT Scheme/Racketの開発者達)が書いた本で、例のOCamlによる「プログラミングの基礎」の元ネタになってる本です。
Racket本体からも[ヘルプ] -> [関連するウェブサイト]から辿れます。



> 2023/02/16

まず、単純に複数の画像を配置させたい、のなら候補としてはplace-images関数がある。

あと、直接関係ないけど、mapfoldに関するテクニックをもう一度まとめておこう。今だと「関数がファーストクラスオブジェクト」である、PythonやRubyが出て来たんで、Lispだけのテクニックじゃなくなったけど、何故に長い間「高階関数がある」Lispが最強と目されてきたか、の理由の一つだ。
mapfoldを使いこなし始めてる現在だからこそ、押さえておくべきテクニックで、かつPythonやRubyなんかの(厳密に言うとRubyは違うけど)「関数がファーストクラスオブジェクト」のプログラミング言語なら流用可能なテクだ。

例えば通常、mapを使う場合、

(map (lambda (x)
 (fun x)) '(データ0 データ1 ...))

と捉えてる。データ0とかデータ1とかは「数値である」とか「文字である」とか、そういうデータとして捉えてるよな。
しかし、「関数がファーストクラスオブジェクトだ」と言う事は、関数自体もデータだ、と言う事だ。
っつーことは、何らかのデータに関して「色んな関数を適用したい」場合、次のように書いても構わない、と言う事になる。

(map (lambda (fun)
 (fun data)) `(,fun0 ,fun1 ...))

目的によっては「この形式でも構わない」と言う事を受け入れよう。
この観点がmapfoldを使う際での「ステップアップ」に繋がる。
fold/reduceに関しても同様で通常、

(fold (lambda (y x)
 (fun x y)) init '(データ0 データ1 ...))

と書いて、データは「数値」とか「文字」とか「文字列」だけ、って思いがちだけど、「関数がファーストクラスオブジェクト」である以上、データ0、データ1、・・・に関数が入っても構わない。
つまり、

(fold (lambda (fun x)
 (fun x)) init `(,fun0 ,fun1 ...))

と言う記述法は「充分アリ」なんだ。これはinitと言う初期値データに対して「色んな関数を連続適用」している、って意味になる。

このfold/reduceの使い方、は2htdp/imageでの「画像関数合成」では結構役立つと思うんで、覚えておいた方がいいと思う。init、つまり基底となるバックグラウンドに対して、色んな「描画関数」(例えば風景毎、とかキャラ毎に別々に作った描画関数)を「連続適用」するのに向いた形式だ。
もちろん、place-images単体で「複数画像を同時にレイヤーとして表示する」のと使い分ける必要はあるんだけど、色んなレイヤーを重ね合わせたい場合、「描画関数自体をリストの要素として」扱う、ってのはかなり見通しの良い設計になると思う。

例えば12歳からはじめる ゼロからの Racketゲームプログラミング教室(大嘘) その5で、描画関数をまとめるplace-world関数はRacket関係での推奨通り

;;; ウィンドゥ表示

(define (place-world w)
 (place-branches
  (world-branches w)
  (world-color w)
  (place-message
   (world-message w)
   (message-area
    (place-characters
     (world-characters w)
     (place-back
      (world-back w)
      *window*))))))

と書いた。
でも、foldrなんかを使うとこう書く事が出来る。

(define (place-world w)
 (foldr (lambda (fun arg init)
    (apply fun (append arg `(,init))))
   *window*
   ;; 関数のリスト
   `(,place-branches ,place-message ,message-area
           ,place-characters ,place-back)
   ;; 関数の引数のリスト
   `((,(world-branches w) ,(world-color w))
    ,(world-message w)) ()
    (,(world-characters w)) (,(world-back w)))))

さて見た目的には両者ともある程度の「複雑さ」があるんで、変わらないかもしんない。
しかし、後者の場合、例えばplace-worldが仮に2つの引数(関数と関数の引数のリスト)を受け取るように改造すれば、それらのリストに対してconsしたりcdrしたり、言わば描画を「操作する」事が可能になるけど、前者のオリジナル版だとそう言う自由度がない(※1)。
こういうカンジで、高階関数の引数リスト自体に「関数を仕込む」と言うのは有用なテクニックになるわけだ。

※1: big-bangto-drawマクロworldを引数に取る一引数の関数をクロージャとして受け取るんで、一見画餅に見えるが、要は、多引数関数を設計してworldを引数とするクロージャを返すように改造すればいいだけ、の話になるだろう。
  • Xでシェアする
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

最近の「RE: プログラミング学習日記」カテゴリーもっと見る

最近の記事
バックナンバー
人気記事