見出し画像

Retro-gaming and so on

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

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

ただ、個人的な意見を言うと、このStyle Guideはある意味極端で(笑)、賛否両論あるんじゃないか、とか思う。
と言うか、あくまで「RacketのStyle Guide」であって「SchemeのStyle Guide」ではない、って事だ。
Racketは色々と便利機能があって、「分かって使う分」にはいいんだけど、他のSchemeに移動する可能性がある場合、ちとどうなんだ、って思うトコがあるんだよな(例えばGaucheを使う、とか)。
もちろん、Racket製作者側から言うと「Pythonみたいにずーっと使い続けていて欲しい」ってのはあるだろうけど。
ちと見ていってみようか。


 Racketのお作法が途中だったので続きを読む。あ、Ifは推奨されてないんですね。確かにBeginは面倒だったもんなぁ・・

「暗黙のbegin」の話。
ただし、前にも書いたけど、オリジナルのLispの設計者だったマッカーシー博士が「あれは失敗だった」と言う、本人にとっては痛恨の設計ミス(笑)。
コンピュータ関連の話だと、しばしば、「設計した本人」が失敗だった、って言ってるモノが普遍化して残る、って言う一つの例になっている(笑)。
もう一つはテキストエディタのvi。あれも設計者のビル・ジョイは「もっとマジメに作れば良かった」って後悔してるらしい(笑)。本人が後悔してても、どういうわけか生き残って愛用者が(UNIXと言う狭い世界にせよ)大勢いる(笑)。
まぁ、ifが推奨されない、と言うより「beginを書きたいか書きたくないか」と言うトコロだな。
もちろん、分岐の枝が2つ以上ある場合はcondもしくはcaseを素直に使った方がいいでしょう。
問題は形式的に分岐の枝が2つしかない場合。この時、condとかcaseだと大仰しく見えはするんだよな。
結局、まずはそこに書いてる通り、ともう一つのケースがある。この2つの時に限った場合はcondとかcaseを使った方が良いパターンだ。

  1. ifを使う時に、節の中にbeginを書かなきゃならない時。
  2. 条件節の結果を使いまわししたい時(アナフォリックなパターン)。
1. は本文に書いてある通り。2. は次のような事だ。
Lispには真偽値がない。より正確に言うと偽値はあるけど真値がない。偽値以外は全部真だ。
つまり、場合に拠っては、条件節の「返り値」をそのまま利用したい場合がある。
詳しくは、例えばR7RSのcondの項とか見て欲しいんだけど、Scheme系言語ではこんな事が出来る。


condの条件節では、連想リスト'((a 1) (b 2))にキー'bが含まれたデータがあるかどうかを調べている。このケースだと「存在する」わけだけど、真を返す代わりに'(b 2)ってデータが返ってくるわけだよな。「偽値じゃないから真」なわけだ。
こういうパターンが散見するわけだけれども、フツーの言語で書くパターンだと次のように書いちゃうだろう。


これでも動くわけだけれども、(assv 'b '((a 1) (b 2)))ってのを2回書かないとならない
これは結構嫌なんだよな。
それを避ける為に、Schemeのcondにはアナフォリックな機能がある。つまり、条件節で演算された「結果」を参照する機能だ。


この書き方(=>を使う)の場合、cadrってのはクロージャなんだけど、「条件節から返ってきた値にcadrと言う関数を適用しろ」って意味になる。つまり、この時の実行節にはクロージャが入る、ってのが前提だ。
なお、ポール・グレアムの「アナフォリックマクロ」っつーのは、ぶっちゃけて言えば、このSchemeの「機能」をANSI Common Lispで実現したい、ってのが根っこにある要望だ。
ANSI Common Lispには、実はこの、Schemeのcondcaseの便利機能がない、と言うそれだけの話なんだ。
この2つ目の機能は実は良く使う。いや、良く使うと言うより、明らかにこのテのパターンが多いんだ。それでそれをシンプルに解決するのはifだと力不足だ、ってのが事実だ。
いずれにせよ、この2つのパターンを見かけた時にはcondcaseを使った方が結果シンプルになるだろう。

ローカル定義はついつい忘れてしまうので意識するようにしないとなぁ

いや、忘れていいと思う(笑)。
これ、どっちかっつーと、「Lisp慣れしてない人に対するサジェスチョン」にしか見えないんだよな(笑)。
いつぞや書いたけど、そもそもLispって構造がネストしてるモンであって、いや、言い換えるとネストがプログラムを決定してるようなトコがあるんだよな。
何故か、っつーとLispのS式ってのは構文木そのものだからだ。僕らは例えばCのコンパイラやPythonインタプリタが、テキストエディタに書かれたソースコードを弄って「抽象構文木」を生成してるのを、その「抽象構文木」を直接書き下してるような事をしてるわけだ。

他の言語ならコンパイラが構文解析して内部に作られる構文木を、 Lispでは直接プログラムとして書き下すわけだ。 

あるいは、川合史朗さんも似たような事を書いている

S式は言ってみれば言語の構文木そのものです。普通の言語では、処理系の フロントエンドにある構文解析器が、「人間に優しい」文法を「機械が理解しやすい」 構文木に変換します。ということは、Lispは機械にやらせれば良い構文解析を わざわざ人間がやっているってことになります。

いずれにせよ、LispのS式がいわゆる「抽象構文木」である以上、これは木構造であり、本質的にネストは避けられないわけだ。
言わば、上でのサジェスチョンと言うのは、あくまで表面的なトリックであって、Lispに根ざした本質じゃないと思う(※1)。

もう一つ言うと、例えば2番目の例で「bad」にされてる例を見てみよう。


これは言うほど「bad」なんだろうか?
そもそも、Lisperがlambda式、翻ると無名関数を愛用する理由は、単純に名前を付けたくないから、だ(笑)。
無名関数は無名である事に価値があるんだよ(笑)。
これは逆にこうも言える。使いまわしする前提じゃない関数は名前を付けなくてもエエやな、と。そして使いまわしする前提の関数なら名前を付けた方がいい、と。
この例だと、使いまわしするかしないか分からん。仮に使いまわしするんだったら、ローカル関数に閉じ込める必要さえない、って事にならないだろうか。


いずれにせよ、「何でもかんでも名前をつける」ってのはむしろC言語的なやり方なんだよな。個人的には「1回しか使わんモノ」に名前なんざ付ける必要って無いと思ってる。
変数も関数も2回以上使う可能性があって初めて名前を付ける価値が生じるんだ。考えてみれば当たり前でしょ?
だから星田さんが、「ローカル定義はついつい忘れてしまう」っつーのは、むしろ、順調にLisp脳になってる、って事だと思う(笑)。
いや、マジな話で。
無駄な局面では無駄な事を我々はしないのだよ。

 う、なんだこのGoodの定義は・・カリー化について言及されてる部分があって立ち止まる。

まずカリー化から。
ぶっちゃけ、話は面白いんだけど、あんま「ソフトウェアを書く側」には役に立たない話なんだよな(笑)。
これはどっちかっつーと「関数型プログラミング言語を設計する人向けの話」であって、パンピーのユーザーにはあまり関係がないんだ。
少なくとも、個人的には「これが実用上役立った」って事は一回もない(笑)。他の人で「役立った」って人はいるかもしれんけどさ。

まずラムダ算法の話から。
ハスケル・カリーって人が元々定義したラムダ算法での関数、ラムダってのは

  1. 引数(っつーか独立変数か)を必ず1つ取らなければならない。
  2. 引数は2つ以上は取れない。
って縛りがあるのね。特に2.は強力でしょ。
じゃあ、ラムダ式で多変数関数は書けないのか、と言うと、それを書く為には1引数関数を組み合わせて書く、って技法が提案されてて。
それがいわゆる「カリー化」ってそれだけの話。

1を3で割るカリー化の例

これがカリー化、だとしても、例えば2引数関数を設計したいのに毎回毎回こんな風に書かなアカン、とか言われれば実用的には「ウゲぇ」じゃない(笑)。
やってられんぞ、と。
だからWeb検索してみても、理論上の話で「ふむふむ」って例ばっかで、「これでプログラムを書いたら書きやすかったです!」なんて話は一個もない(笑)。
少なくともそんなの見つけられなかったなぁ。

いや、実際、関数が1引数しか取れないプログラミング言語ってあるんだわ。
Haskell、とかな
でも、Haskellでさえ、構文糖衣(Syntactic Sugar)でフツーに多引数関数作れるようになってる。内部的にはカリー化したブツに置き換えられるわけだけれども。
要するに、「理論的に関数型言語自体作りたい」って人以外に特に役立たないんだよ(笑)。
言っちゃえばユーザー側がそんな情報知ってても意味がない、っつーか(笑)。
せいぜいキャバクラ行った時にキャバ嬢の姉ちゃん辺りに話せる自慢話くらいにしかならん(笑)。断言出来る(笑)。
キャバ嬢の姉ちゃんだったら「この人つまんない話してるわねー」と内心思ってても、一応ニコニコして聞いてくれるからな(笑)。

「すみません、フルーツ頼んでいいですか?」

とか言いながら(笑)。

ちなみに、Racketのforは見た目と違い、実はPythonで言うトコのリスト内包表記の一種だ。
つまり、SRFI-42の別バージョンとなっている。

※1: あと、厳密な話をすると、internal defineってのはある意味、関数型プログラミングらしくない「逐次実行」をアテにした方式だ・・・letletrecより明らかに「見やすくなる」のは事実なんで、悩ましいトコだ。
  • Xでシェアする
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

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

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