Gauche:object-applyの例
object-applyの例
Lispを初めてさわったのは何かの演習のときだったと思いますが、
リスト(1 2 3 4 5)の先頭を取り出してください。 > (car (1 2 3 4 5)) ERROR こういうときはリストにクォートをつけます。 > (car '(1 2 3 4 5)) 1 次はx=100,y=200,z=300のとき、リスト(x y z)の先頭を取り出してください。 > (setq x 100) > (setq y 200) > (setq z 300) > (car '(x y z)) x 文字が出てきてしまった。クォートがいらないのか? > (car (x y z)) ERROR やっぱりエラーか~。だめだ... (しばらくして...) もうこの文字xを数値に無理やり変換したらよいのでは? > (eval (car '(x y z))) 100 何とかできた。でもこれで複雑なプログラムを作るのは。。。
という感じで数字になったり、文字になったり、エラーになったりするリストにえらく苦労した記憶があります。
時は流れて、Gaucheではobject-applyを使って任意のオブジェクトを適用可能にできるため、
> (car (1 2 3 4 5)) 1
ということも、
> (define x 100) > (define y 200) > (define z 300) > (car (x y z)) 100
ということも、object-applyにメソッドを定義すれば可能になると分かりました。
実装してみたモジュールが以下になります。
https://github.com/Hamayama/noqlist
それで、結論としてはそのページにも書いたように、デバッグが困難になるのでだめそうな感じでした。
ただ、それだったら、変数に思っていない型のデータが入ってもエラーにならなかったりとか、
データのつもりがプログラムと解釈されてもエラーにならなかったりとか、
そういうケースもありえるので、どこまでならエラーにならなくても許されるのかというのは、
人それぞれのような気もします。
なぜLispやSchemeの仕様が広く受け入れられて、皆がそれに従っているのか?
疑問を持つ人はいないのか? いまひとつ釈然としない感じは今もしています。
hamayama(2014/09/09 19:29:12 UTC)
quoteについて
Shiro(2014/09/09 23:58:30 UTC): quoteとは何か、というのは実はLispのアイデンティティの 根幹に関わってくる話で、誰か有名なCLerも quoteがわかったとき始めてLispが何かわかった、みたいなことを言ってたような。 普通の言語として見た場合、リストをquoteするよりも、むしろリストコンストラクタ構文を用意するのが 自然なんですね。例えば {} をリストコンストラクタとすれば、
(define a {1 2 3}) => (1 2 3) (define b {4 a 5}) => (4 (1 2 3) 5) ; {}はコンストラクタ呼び出しなので中も評価される
もちろん一歩進めて、リストの表記が(1 2 3)なんだからコンストラクタも()にしたらいいじゃん、 と議論してもいいんですが(例えばHaskellなら表記もコンストラクタも[]ですね)、 そうすると構文に使う()とぶつかってしまいます。
つまり、「他の言語」でリスト表記とリストコンストラクタを同じに出来るのは、 言語の構成要素としての構文、いわゆる「コード部分」がリストじゃないからなんです。
逆に、LispがLispたる所以は、コード部分がリストであること、なんですね。
コードもデータもリストで、ただし住む世界が違う。そして、マクロやevalや言語処理系は、 コードvsデータ、という関係を一段ずらして、普通の「コード」の部分を「データ」に持ってきたり、 「データ」部分を「コード」にしたりする。
コード(program)とデータの関係を例えば⋉と書くと、コードを扱うコード、metaprogramと元の プログラムの間にも同じ関係が成り立ちます。
program ⋉ data metaprogram ⋉ program
この「同じ関係」が決定的に重要で、これによってプログラムを解釈するプログラムを解釈するプログラム… というふうに上の方向にも、またデータをプログラムとして見たときにそれが解釈するデータをプログラムとして見たときにそれが解釈するデータ… というふうに下の方向にもいくらでも積み重ねてゆけます。
... ⋉ a ⋉ b ⋉ c ⋉ d ⋉ ...
こういった無限に続く関係性が、一つの ⋉ という有限な関係で表現できる、というところが Lispの原点であり、quoteはこの関係の左右、異なる世界の住人を一緒の場所に 表記するための決定的な構文なのです。
- hamayama(2014/09/14 12:28:50 UTC):ありがとうございます。前よりも理解できそうです。
上の話は本当に奥が深くて、演習のときにこのような説明があれば、と思います。
少し不思議な感じがしたのは、例えばコードをリストで表すと決めたにしても、
(eval (手続き 引数 ...)) のように「データを実行する」みたいに考えるのが普通ではないか、ということです。
つまり、リスト自体は (データ1 データ2 ...) とそのまま表記するようにして、
「実行する」というほうを eval なり クォート なりで区別するようにしたくなると思うのです。
どうしてLispを作った人々は、「実行する」方を重視して、「データ」の方は
クォートで実行を抑制するという選択をしたのでしょう。
結果として、上の演習のように初心者へのハードルが発生したと思うのですが。。。
Shiro(2014/09/14 19:25:52 UTC): (eval (手続き 引数 ...)) だと「(eval 何か)」の部分が依然として 「コード」ですよ。そこをリストで表現する、とやっていったら元の木阿弥じゃないですか? プログラムというのは実行するためにあるのだから、 「書かれていることをコードとして解釈する」という部分は絶対に必要で、そこは譲れない。 でも、その「プログラム」を、別のプログラムが扱う対象の 「データ」として見ることができると、「プログラムを扱うプログラム」が何ら特別なものではなくなる、 という経緯です。
- hamayama(2014/09/16 01:08:08 UTC):確かに「「(eval 何か)」の部分が依然として「コード」」ですね。。。
考えたことは、
「コードはリストそのままで、「'」でコード→データのシフトを行う」
ということは、例えば、
「データはリストそのままで、「#」でデータ→コードのシフトを行う」
というようにしても、同じ関係を成り立たせることができるのではないか、ということです。
この記述にした場合、例えば、
(car '(1 2 3 4 5))
は、
#(car (1 2 3 4 5))
になります。
しかし、実際にはプログラム中にデータを埋め込む機会がそんなに多いわけではなく、
「プログラムというのは実行するためにある」ことを考えると、
元の仕様の方がコードが短く書けて、理にかなっていそうです。
今回、過去のはまり経験から、クォートなしでデータのリストを書けたほうが自然ではないかと考えて、
object-apply を使って実装してみました。
結果としてコードとデータがまざってしまい、プログラミングが困難なものになりました。
そして、クォートによる「コードをデータにシフトする」という見方を教えてもらい、
少し回り道もしましたが、Lisp/Schemeに対する理解を深められたと思います。
今後も、気がついたことがあれば、できる範囲でいろいろ試していこうと思います。
Shiro(2014/09/16 03:09:21 UTC): 元々書かれているものがデータで、実行したいコード部分 だけを特別にマークする、という選択ももちろんあります。代表的なのは文書の中にちょっと 可変部分や実行部分を埋め込みたいというやつで、テンプレートプロセッサとか、 TeXも広い意味でそちらに属するでしょう。どちらの設計が良いかは、 データ部分とコード部分のどちらが多いかに依存するでしょうね。