トップページ⇒tech
1001コメント355KB

CommonLisp Scheme Part12

■ このスレッドは過去ログ倉庫に格納されています
0001デフォルトの名無しさんNGNG
過去スレ
Part1: http://piza2.2ch.net/tech/kako/987/987169286.html
Part2: http://pc.2ch.net/tech/kako/1002/10025/1002584344.html
Part3: http://pc.2ch.net/tech/kako/1008/10082/1008220265.html
Part4: http://pc.2ch.net/tech/kako/1016/10162/1016211619.html
Part5: http://pc3.2ch.net/tech/kako/1023/10230/1023091882.html
Part6: http://pc3.2ch.net/tech/kako/1031/10315/1031560687.html
Part7: http://pc5.2ch.net/tech/kako/1042/10421/1042167213.html
Part8: http://pc5.2ch.net/tech/kako/1058/10582/1058263391.html
Part9: http://pc2.2ch.net/test/read.cgi/tech/1069594582/
Part10: http://pc5.2ch.net/test/read.cgi/tech/1075630259/
Part11: http://pc5.2ch.net/test/read.cgi/tech/1091456033/

関連リンクは>>2-10あたり
0322デフォルトの名無しさんNGNG
dequeが欲しけりゃリストの先頭と末尾を指すconsを一個持てば作れるけどな。
破壊的操作が必要になるのであまり嬉しくないけど、
計算量的にそれが必要ならやればいいだけの話。


0323319NGNG
>>321
いやそういうことではなくて…。後置記法Lispの場合、consで先頭に
突っこんでいくと逆さまになるのも気にならなくなるのでは、
ということです。
0324デフォルトの名無しさんNGNG
そんなあなたにDylanを





いえ、すんません。巣に帰りまつ(;´Д`)
0325デフォルトの名無しさんNGNG
アホすぎ。reverseかけるのは気になるならないの問題じゃないんだが。
0326デフォルトの名無しさんNGNG
(define (copy-list1 l)
(let loop ((l l) (r '()))
(if (null? l) (reverse r)
(loop (cdr l) (cons (car l) r)))))

(define (copy-list2 l)
(let loop ((l l) (c (lambda (x) x)))
(if (null? l) (c l)
(loop (cdr l) (lambda (x) (c (cons (car l) x)))))))

(define (copy-list3 l)
(if (null? l) '() (cons (car l) (copy-list3 (cdr l)))))

(define (copy-list4 l)
(let ((n '(())))
(let loop ((l l) (r n) (end n))
(if (null? l) (cdr r)
(begin (set-cdr! end (cons (car l) '()))
(loop (cdr l) r (cdr end)))))))

速度は
copy-list1 > copy-list4 >>> copy-list2 >>>>>> copy-list3
くらいでした。copy-list4 はなんで遅いんだろ。
0327デフォルトの名無しさんNGNG
>>326
リストの長さや処理系によっても随分違うものになるだろうから
それらを特定しない比較には意味が無いような
0328デフォルトの名無しさんNGNG
copy-list4はrが冗長なパラメータだろう。それはそれとして、
リストの長さを変えても傾向は変わらなかったのか?
0329デフォルトの名無しさんNGNG
(define (copy-list5 l)
(apply list l))
0330デフォルトの名無しさんNGNG
>>328
ほんとですね。とりあえず、

(define (fact f n)
  (if (= n 1) f (lambda (x) ((fact f (- n 1)) (f x)))))

(define (num-list n)
  (let loop ((n n) (r '()))
    (if (zero? n) r (loop (- n 1) (cons n r)))))

として

((fact copy-list1 X) (num-list Y))
みたいな感じで計ってみました。
Yを大きくすると copy-list4が速くなると思ったんですが、
copy-list1の方が速かったです。処理系は Gaucheです。
他の処理系でも試してみます。
0331デフォルトの名無しさんNGNG
cons,cdr,set-cdr!が同じ時間で実行できるとして
copy-list1,copy-list4がそれぞれどれくらいの時間でできるか計算してごらん。

copy-list1はgcの時間をのぞいたら変わらないと思うよ。
0332デフォルトの名無しさんNGNG
(define (my-reverse l)
(let loop ((l l) (r '()))
(if (null? l) r
(loop (cdr l) (cons (car l) r)))))

(define (copy-list6 l) (my-reverse (my-reverse l)))

としたら copy-list4 > copy-list6 でした。
つまり copy-list1は組み込みのreverseを使っているから速いってことでしょうか。
リストの長さをNとすると copy-list1は 6N、copy-list4 は 5Nくらいの計算量に
なるんですかね。
0333デフォルトの名無しさんNGNG
guileでも同じ様な結果でした。
ちなみに Gauche組み込み対決では
list-copy と (lambda (l) (reverse! (reverse l)))
が互格でした。
こういうリスト全体の処理では組み込みの reverseを使った方がいいってことですね。
あとからリストの後ろに破壊的に要素を足していきたい場合は deque方式と。
0334デフォルトの名無しさんNGNG
>つまり copy-list1は組み込みのreverseを使っているから速いってことでしょうか。
そうです。

copy-list1は2回cdrをたどってるから2N、copy-list4はNで
reverseがどんなに速くても、copy-list1はNより速くならないと考えるかもしれないけど、
copy-list1は6N、copy-list4は5Nになる。
copy-list1の内reverseにかかる部分が3Nで、コンパイルしたら2倍から10倍で実行できるとすると
4.5Nから3.6Nで実行できるわけです。

実際にどれくらいの速度差で実行してるか調べてみたら?
0335デフォルトの名無しさんNGNG
3.6N -> 3.3N
0336デフォルトの名無しさんNGNG
(defun copy-list (list)
(mapcar #'identity list))

mapcar って組み込みだろうから、ごちゃごちゃコード書くより一番速い気がする。

ご参考
http://www.lisp.org/table/style.htm#efficiency
0337デフォルトの名無しさんNGNG
(define (copy-list l) (append l '()))
0338デフォルトの名無しさんNGNG
そんなに速度気にするんならコンパイルできる処理系で
コンパイルした方がいくない?
0339デフォルトの名無しさんNGNG
自作のScheme処理系のためのコンパイラを書いてるんですが、named let は
次のような感じで変換すればオーケーでしょうか?
letrecをまだ用意していないもので。

(let loop ((a foo)
      (b bar)
      (c baz))
 body)
=>
(let ((loop #f)
   (a foo)
   (b bar)
   (c baz))
 (set! loop (lambda (a b c) body))
 (loop a b c))
0340(define (´∀`) 'マターリ)NGNG
(((lambda (f x) (lambda y (apply (x (f f x)) y)))
(lambda (f x) (lambda y (apply (x (f f x)) y)))
(lambda (loop) (lambda (a b c) body)))
foo bar baz)
0341デフォルトの名無しさんNGNG
>>339
foo, bar, bazのスコープにloopが入ったらまずいんでない?
0342デフォルトの名無しさんNGNG
>>341
入るのでしょうか? 入らないように思えるのですが。
gosh> (define loop '|(゚∀゚)アヒャ|)
loop
gosh> (let ((loop #f) (a loop) (c (lambda () loop))) (list a (c)))
(|(゚∀゚)アヒャ| |(゚∀゚)アヒャ|)
0343デフォルトの名無しさんNGNG
>>340
一見しただけでは何が何やらですが、理解に努めてみました。

> (lambda (f x) (lambda y (apply (x (f f x)) y)))
これをF
> (lambda (loop) (lambda (a b c) body)))
これをL
とおくと
((F F L) foo bar baz)
になって、少し展開すると
((let (loop (F F L))
(lambda (a b c) body))
foo bar baz)
みたいになりますね。
このbodyの中でloopを呼ぶと((F F L) a b c) のに戻って繰り返すわけですね。
面白い。こういうのをストリームというんでしょうか。
0344デフォルトの名無しさんNGNG
>>211
ホントですか?
Cygwin 1.5.12-1
と
Gauche0.8.3 と Gauche-gtk-0.4.1
だと
gauche-glib.cのmakeでエラーがでてしまいます。

どーやったらコンパイルできるようになるのでしょうか?
0345339NGNG
コンパイラが初期化用手続きを吐けるようになりましたので記念上げ。
# いまんとこコンパイラを動かすにはGaucheが必要だったりするんですが

ところで自由変数を参照しない手続きを呼ぶ用語に困っているのですが、
何か適当な用語はあるでしょうか。
環境を持たないクロージャなんですが、組み込みの手続き(コンパイラが即値で
書き込む)を利用するので副作用があって関数とも呼びにくいのです。
ちなみに大域環境はありません。
0346デフォルトの名無しさんNGNG
完成おめ。
これからブートストラップしてくんだね。

ところで環境を持たないクロージャとは、単純にトップレベルで
作成するような関数のことかね?
すなわちその関数内部の変数参照がローカル内で完結している関数
という解釈でいいのかな。
それとも、環境を持たないクロージャというのを、
限定構文を使うなりで明示的に宣言して作成するということかね。
副作用がSchemeからは見えず、処理系の内部的な話なのであれば、
特に区別する必要もない気がするが。
明示的なものであれば、例えばCにトランスレートするのが前提の関数なら
cfuncとかcdefineとか機能や意図に基づいた名前を付けるぐらいしか
思いつかないが。
あとは既存の処理系の名前を漁ってみるしかないんじゃないかな。
0347339NGNG
>>346
どうもです。

> ところで環境を持たないクロージャとは、単純にトップレベルで
> 作成するような関数のことかね?

そうです。レキシカル変数(引数か、内部で積む環境フレーム上の変数)だけ
使うもののつもりでした。

0348デフォルトの名無しさんNGNG
consとかcarとかつかわないの?
0349デフォルトの名無しさんNGNG
>>4
次スレからは

http://swiss.csail.mit.edu/classes/6.001/abelson-sussman-lectures/

もテンプレに加えてちょ。 ビデオ講義がある。 居ながらにしてMITの講義が見れるぞ。
0350デフォルトの名無しさんNGNG
過去スレ以外は別ページにすべきだよ。
0351デフォルトの名無しさんNGNG
まとめサイトがありゃいいけど
おれにはそんなの作る気力がない
まあ当面は>>2以降にだらだらでいいやね
0352デフォルトの名無しさんNGNG
>>349
他の講義のビデオも有ったりするの?
0353デフォルトの名無しさんNGNG
>>344
Gauche 側の gauche.h の SCM_EXTERN から __declspec(dllimport) を削って
Gauche-gtk 側は libgauche-uvector をリンクするよう追加するだけ。
最近の ld と gcc なら dllimport は無くてもよしなにやってくれるから。
0354デフォルトの名無しさんNGNG
冬休み暇なやつがまとめページ作ります
0355デフォルトの名無しさんNGNG
>>352
http://ocw.mit.edu/OcwWeb/index.htm

にMITの講義録やシラバスがあるが、ビデオは数えるほどしかない。 将来的には
もっとビデオを増やすらしい。 

ここはLispスレだし、その方面のバイブルがタダで手に入って、かつ、講義まで全部見れるんだから
世の中便利になったと思います。 

個人的にはSICPは冗長な気がする(特に、1〜3章)けど、4,5章は真面目に読む価値があると
思う。 実際に自分で手を動かして、Schemeのインタプリタやランタイム&コンパイラをJavaや
C++で実装するとホントに理解が深まる。 CSを勉強するときには実際に手を動かした時に学んだ
ノウハウが大きな財産になるから、暇な人はトライしてみる価値がある。
0356デフォルトの名無しさんNGNG
CSって何?
0357デフォルトの名無しさんNGNG
>>356
今年マリノスが勝って終わったやつ。
0358デフォルトの名無しさんNGNG
東経110度でスカパーとかやってるやつ。
0359デフォルトの名無しさんNGNG
無線とかで最初にいうやつ。
0360デフォルトの名無しさんNGNG
シャア専用の略
0361355NGNG
マジレスするとComputer Scienceの略。 コンピュータ・サイエンス専攻なら英語で

I'm a CS major at ** university. で通じるよ。
0362デフォルトの名無しさんNGNG
Computer Systemかと思った。
0363デフォルトの名無しさんNGNG
>>345
> ところで自由変数を参照しない手続きを呼ぶ用語に困っているのですが、
> 何か適当な用語はあるでしょうか。

「自由変数を参照しない手続き」といえばコンビネータだけど
ちょっとニュアンスが違うような気もしないでもない?
0364デフォルトの名無しさんNGNG
「副作用があるから関数とは呼びにくい」って書いてなかったか…?
コンビネータは、普通はあまり副作用のないイメージが。

「閉じた式」(=自由変数のない式)の類推で、「閉じた手続き」じゃ駄目?
0365デフォルトの名無しさんNGNG
関数じゃないならSubとかProcedureでいいじゃん
0366デフォルトの名無しさんNGNG
凄く初歩的なことのような気がするのですが質問良いでしょうか…

(define aaa "hello, world")
(define (bbb) 'aaa)

(ccc (bbb)) => "hello, world"

になるような手続きcccってあるんでしょうか。
evalしかないですか?
0367デフォルトの名無しさんNGNG
あとはマクロぐらいじゃない?
0368デフォルトの名無しさんNGNG
quoteをつけることによって、evalしないわけだから、この場合明示的にevalしなきゃ
いけないんじゃない? sicpの4章を読みましょう。
0369366NGNG
そうですか。ありがとうございます。

とりあえずSICPも読んでみます。
0370デフォルトの名無しさんNGNG
束縛をつかうからダメなだけで、シンボルと値の対応表を別に作ってassqすればいいんでないかい?
0371344NGNG

>>353
遅くなりましたが、教えていただきありがとうございます。
これから試してみます。
0372デフォルトの名無しさんNGNG
関数で、結果が環境に依存せず引数だけで決まり、また、
副作用ももたないものを形容する言葉はありますか?
+や-などがその仲間だとおもいます。よろしくお願いします。
0373339NGNG
明けましておめでとうございます。

返事がおくれてすみません。
>>363,364
さすがにコンビネータはちょっとそぐわない気がいたします。
閉じた手続きはいいかも知れません。closed-procedureでしょうか?

>>348
そういうのは組込みの手続きを用意していてそれをコンパイラが埋め込みます。
初期化段階ではLisp/Scheme的な大域環境が(シンボルも)存在しないので。

>>372
それを関数(function)と呼ぶのではないでしょうか。
副作用があったり環境に依存するものを含めた総称がprocedureではないかと思います。
0374デフォルトの名無しさんNGNG
>>373
> それを関数(function)と呼ぶのではないでしょうか。
> 副作用があったり環境に依存するものを含めた総称がprocedureではないかと思います。
すみません、書き忘れました。Common Lispに限定しますと、規格書やコミュニティー内でも、
そのような区別はされていないようです。
(functionpなんてpredicateはそういう区別をしません。)
0375339NGNG
Common Lispの場合はそうですね。

関数が手続き一般を指す場合、"pure function" というのが使われることが
多そうな気がします。元はfortran用語でしょうか。

0376デフォルトの名無しさんNGNG
>>375 なるほど。ありがとうございました。
0377デフォルトの名無しさんNGNG
>>373
(define (hoge x y) (cons x y))
(define (cons x y) ...)
はどう処理されるの?
0378339NGNG
>>377
いまのところ、そういうことはしない、というのが答です。
というのも、フルセットのScheme環境を用意するための「ローレベルScheme」
みたいなものが趣旨なので。シンボルも環境もこれを使って書くわけです。
# 今のところベクタも文字列もシンボルもありません

現状、大域変数はコンパイル単位で完結し、かつちょっと情けないですが1パスで
> (define (hoge x y) (cons x y))
ここでconsは組込み定数の参照("SCM_OBJ(&cons)"みたいな)に置き換えられ
> (define (cons x y) ...)
以降はconsに割り当てられた大域変数を参照します。
必要なら、lambda中の大域変数の操作をいったん擬似命令として出力する2パスで
今の条件でも(コンパイル単位で完結するのは変わりませんが)例のようなケースの
参照を普通のSchemeのようにできるとは思います。

今のところ大域変数の実体はコンパイル単位全体を包む最下段のレキシカル環境
フレームという身も蓋もない実装です。一段落ついたら、
defineされる大域シンボルのうち定義だけされるものと
変更されるものを区別して、後者のみCの変数を記憶場所に割り当て、
前者は定数化して参照を直接埋め込むようにする予定です。それが済めば
コンパイル単位間で大域変数を共有することも(必要性はともかく)できるはずです。

共通の大域環境を参照する普通のSchemeのコードをどうコンパイルするかは
そのうちシンボルや大域環境をローレベルschemeで書きながら決めます。

0379デフォルトの名無しさんNGNG
Daniel P. Friedman: A Celebration
ttp://www.cs.indiana.edu/dfried_celebration.html
0380デフォルトの名無しさんNGNG
>>380
Schemeで著明な人達の写真が見れるね。
Guy Steeleの顔ぐらいしか知らなかった。
0381デフォルトの名無しさんNGNG
ドレッドヘアーの黒人は誰?
0382デフォルトの名無しさんNGNG
lisper が書く情報科学の wikibooks を見てみたい。
0383デフォルトの名無しさんNGNG
どなたかSECDR-Schemeの再ウプきぼんぬ。
小さくて速い処理系を探してまして、最も有望なBitとSECDR-Schemeのポインタが切れていたもので。
0384デフォルトの名無しさんNGNG
リンクは切れてたけど適当にURL推測したらあったよ>SECDR。
去年の話だけど。
0385デフォルトの名無しさんNGNG
型付きラムダ計算について書いてある資料で、
何かお勧めのやつあれば教えてください。
サイトでも教科書でも構いませんので……

MLのスレで聞いたほうがいいのかな?
0386デフォルトの名無しさんNGNG
型付きラムダ計算について書いてある資料で、
何かお勧めのやつあれば教えてください。
サイトでも教科書でも構いませんので……

MLのスレで聞いたほうがいいのかな?
0387デフォルトの名無しさんNGNG
>>386
MLの偉い人で、大堀先生の本はどうだろう。

共立出版 情報数学口座9「プログラミング言語の基礎理論」
http://www.jaist.ac.jp/~ohori/texts/index.html

この本に
形無しλ計算 → 型付きλ計算 → 多相型λ計算 → ML
の順番で説明されてたと思う。



0388デフォルトの名無しさんNGNG
>>387
その本にもあるかわかんないけど、
おれ論理式がよくわかんなかった。
でかい分数表記のやつ。
つーかあの辺理解できる気がしない。
実際どう動くかコードで示してほしかった・・・なんて無茶か。
0389デフォルトの名無しさんNGNG
>>383
ttp://paw.s2.x-beat.com/up/img/4398.zip
zipの中身がSECDR-Scheme1.0.tgz
これのオリジナルが置いてあった場所はniftyだったと思う。
0390デフォルトの名無しさんNGNG
>>387
レスどうもありがとうです.
とりあえず図書館で探して読んでみるとです.
0391383NGNG
>389
多謝。ドキュメントが詳しいのも良いっすな。
0392紫藤05/01/18 11:24:21
reply to 58:
> Paul Graham, ANSI Common Lisp の練習問題の解答を載せた
>ホームページ等、ご存知でしたら教えていただけませんでしょうか

http://www.geocities.jp/shido_takafumi/pacl2.html
に書いたのでよろしかったら見てください。
せっかく書いたのに誰も見てくれないので宣伝します。
0393デフォルトの名無しさん05/01/20 22:58:06
習作のSchemeもどきの実装を晒しても良いですか?
0394デフォルトの名無しさん05/01/20 23:01:13
>>393
おねがいします。
0395デフォルトの名無しさん05/01/20 23:05:12
初めて作った実装なのでダメダメかもしれません。
とりあえず mini-schemeよりは速いはずです。
継続がなくて、マクロもどきや環境がファーストクラスです。
中間コードにコンパイルする方法を勉強して継続を実装したりしたいです。
http://primes.dnip.net/scheme.cpp
0396デフォルトの名無しさん05/01/21 10:40:10
>>395
たらい回ししてみようか…と思ったら、比較オペレータは定義されてなかった。
まだまだテスト版だからってことでしょうか。

ちょっと見てみると、scheme_oprtクラスとか無駄してるような…。
C++で実装するというのは、C使いの自分には面白かったです。がんばってください。

これだけのコードでまがりなりにも
処理系を作れてしまうというのが、Scheme(lisp)の良さですよね。
0397デフォルトの名無しさん05/01/21 11:44:13
>>396
比較オペレータなどを定義してみました。
scheme_oprtクラスは無駄してるでしょうか? よく分かんないです。
整数や真偽値や基本手続きをポインタではなく即値で埋めこむテクニックを知ったのですが
この先中間コードにコンパイルすることを考えると、今はどっちでもいいのかなあと思いました。

中間コードの話はSICP5章とドラゴンブック以外に参考になる文献はあるでしょうか?
特に継続の実装がさっぱり分からないです。
039839605/01/21 14:45:19
>>397
わっ、反応早い!

scheme_oprtクラスのprintメソッドは、簡単なやり方でも、
とりあえず先にprintf("#<subr ")しておいて、if文でプロシージャ名をprintfして、
最後に">"をprintfしておいた方が、無駄な文字列データが減るはずです。

aplyメソッドも、プロシージャごとに完全なコード書かずに、汎用化するとか。

色んな意味でRHG(Ruby Hacking Guide)とGHG(Gauche Hacking Guide)は
役に立つと思いますが、すでに見ているか。
0399デフォルトの名無しさん05/01/21 16:07:14
>>398
printメソッドの話ですか。そこらへんは適当です。
Rubyのは少し見てましたが、Gaucheのは知りませんでした。
C++は細かいとこが直感的に分らないのでCで書きたくなってきました。

質問なんですが、手続き呼びだしの時、環境をコピーするより名前置き変えの方がいいですよね。
あと、SRFI-17の set!を実装するには全てのオブジェクトのサイズを同じにしないといけないんでしょうか?
0400デフォルトの名無しさん05/01/22 01:57:46
>質問なんですが、手続き呼びだしの時、環境をコピーするより名前置き変えの方がいいですよね。
クロージャーの呼び出しでは引数を束縛して新しい環境を作るけど、これはコピーとはちょと違うし、名前の置き換えというのも何を意味しているのかちょとわからないなり。
>あと、SRFI-17の set!を実装するには全てのオブジェクトのサイズを同じにしないといけないんでしょうか?
SRFI-17のほうは例えば(define x "hogehoge") (set! (string-ref x 4) 3.14)みたいなことはエラーになるべきだから考えなくていいんじゃないかな。
0401デフォルトの名無しさん05/01/22 03:00:08
>>400
>クロージャーの呼び出しでは引数を束縛して新しい環境を作るけど、これはコピーとはちょと違うし、名前の置き換えというのも何を意味しているのかちょとわからないなり。
環境を壊したくないので、クロージャーを呼び出した時点での環境をコピーしてそれに引数の束縛を足して使うという意味です。
新しい環境を作るのにコピーはしないんでしょうか? 名前の置き変えは、環境は一つだけでかぶる引数の名前を全部置き変えるということです。

環境をリストで表現して先頭から新しい束縛を足して、検索するときは先頭から順番に調べるようにすれば、一つのポインタのコピーだけで済んで良いかなあと思いました。
ハッシュと違って検索に束縛の数に比例して時間がかかるのが欠点ですがどんなもんでしょうか。

>SRFI-17のほうは例えば(define x "hogehoge") (set! (string-ref x 4) 3.14)みたいなことはエラーになるべきだから考えなくていいんじゃないかな。
なんか勘違いしてました。ポインタの指す先を変更しないといけないのかと思ってました。
0402デフォルトの名無しさん05/01/22 11:15:08
>>401
>環境を壊したくないので、クロージャーを呼び出した時点での環境をコピーしてそれに引数の束縛を足して使うという意味です
ふむふむそういうことだったか....
最初に、クロージャーの呼び出し時にはその意味での環境のコピーはないなり。すでに存在する環境に引数から作った環境をリンクするだけなり。
次に、lambdaでクロージャーを作るときは環境をスタックからヒープに移動することはあるけれど、元のスタック上の環境は以後は使われなくなるなり。
>新しい環境を作るのにコピーはしないんでしょうか?
引数から新しい環境を作ったら以前の環境に対してリンクを張るだけなり。

このへんは >>3 の"Practical Scheme"が詳しい。
特に実装についてはその中の以下のページが素晴らしいなり。
ttp://www.shiro.dreamhost.com/scheme/stack-j.html
ttp://www.shiro.dreamhost.com/scheme/gauche/memo-stack-j.html
040340205/01/22 11:37:42
>すでに存在する環境に引数から作った環境をリンクする
「すでに存在する環境」はクロージャーの持っている環境のことなり。
>元のスタック上の環境は以後は使われなくなるなり
これは完全にゴミになるということなり(実際デバッグ時には"0xcafecafe"とかで破壊するとよい)

どうもShiroのようにはうまく説明できないのう。
0404デフォルトの名無しさん05/01/22 12:00:50
secd machineでぐぐれば一発でわかると思う。
0405デフォルトの名無しさん05/01/22 12:36:55
>>402
なるほど。ちょっと難しいですが、20%くらい分かった気がします。
名前を検索するときは envポインタからたどって、
一番下の環境->一個上の環境->もう一個上の環境 という風に探していくんでしょうか?
なんか効率が悪いような。環境一つ一つはハッシュなんでしょうか?

>次に、lambdaでクロージャーを作るときは環境をスタックからヒープに移動することはあるけれど、元のスタック上の環境は以後は使われなくなるなり。
なんで使われなくなるのか良く分からないです。これはスタックマシンのVMの話だからまた別の話なのかもしれませんけど。
0406デフォルトの名無しさん05/01/22 13:01:52
SICP 4.1.7にあるような構文解析をしようと思ったんですが、quoteや ifに別の値が束縛されていると困ることに気づきました。
妥協案として実行時に defineや lambdaに出食わしたら、その時の環境を使ってボディを構文解析するというのを思いつきますた。

でもそれでは
(define (my-if a b c) (let ((if if)) (if a b c)))
は解析できても
(define (my-if a b c) (if a b c))
というノーマルなやつが解析できないと思いますた。でも Gaucheでは

gosh> (define (my-if a b c) (if a b c))
my-if
gosh> (define if list)
if
gosh> (my-if #t 0 1)
0

となりました。
というか上のバージョンは実行できなかったです。scmでも実行できなくて、scheme48では定義すらできませんでした。
guileでは実行できました。
という訳で、実行時に defineや lambdaに出食わしたら rootも含めた環境でボディ部を構文解析するというのでいいのでしょうか?
それとも ifを別の名前に束縛してる場合は諦めて、ifが上書きされてないか気合いでチェックして実行前に構文解析してしまうのがいいんでしょうか?
0407デフォルトの名無しさん05/01/22 13:06:23
>>405
>一番下の環境->一個上の環境->もう一個上の環境 という風に探していくんでしょうか?
基本はその通り。
>なんか効率が悪いような。環境一つ一つはハッシュなんでしょうか?
間違いなく効率が悪い。だからインタープリターでも中間コードを使ったり、コンパイラはさらに気合いを入れた最適化をするなり。
>環境一つ一つはハッシュなんでしょうか?
トップレベルだけはハッシュを使う実装が多い。
>なんで使われなくなるのか良く分からないです
これは環境の複製を作って、オリジナルと複製の両方を使うということがないという意味で書いてみたんだけと、説明の書き方が悪かったかなりね。

>>404 secd machineでぐぐれば一発でわかると思う。
いいね〜SECDマシーン。

ちなみに、SECDによるScheme実装にはLispMeもあるなり。こっちは"SECD LispMe"でググルべし。
(LispMeという名前だけどSchemeだよん)
0408デフォルトの名無しさん05/01/22 13:33:54
>>406
(define (my-if a b c)
 (let ((if if))
  (if a b c)))
--> syntax errorまたはunbound variable 'if みたいなエラー

(let ((if list))
 (define (my-if a b c)
  (let ((if if)) (if a b c)))
 (if 1 2 3))
--> (1 2 3)

これはifに束縛値があれば変数として扱い、なければsyntax keywordとして扱っているからで、構文解析するときにはトップレベルから発生しているすべての束縛を記録しておく必要があるなり。これはよくcompile time environmentと呼ばれているなり。

ちなみにgaucheで実行できるのは、
gosh> (define (my-if a b c) (if a b c))
で中間コードに変換されるときにifがsyntaxのifとして中間コードに書き込まれていて後の
gosh> (define if list) の影響を受けないから。
gosh> (define if list) (define (my-if a b c) (if a b c)) (my-if #t 0 1)
とやれば(#t 0 1)が返されるはず。
gosh> (define (my-if a b c) (let ((if if)) (if a b c)))
はやはりエラーになるんじゃないかな?

最後に、どのmy-ifの定義もifと同等の定義にはなっていないよ。これは念のためなり。
040940805/01/22 13:39:37
おっと、
(let ((if list))
 (define (my-if a b c)
  (let ((if if)) (if a b c)))
 (if 1 2 3))
は
(let ((if list))
 (define (my-if a b c)
  (let ((if if)) (if a b c)))
 (my-if 1 2 3))
のまちがいなり。4行も書くとバグがでるなりね(笑
0410デフォルトの名無しさん05/01/22 13:54:59
>なり
ウザイ
0411デフォルトの名無しさん05/01/22 14:05:02
>>408
えっと上のやつは
(define my-if (let ((if if)) (lambda (a b c) (if a b c))))
の間違いです。アホでした。どちみちエラーなんですけど。
>これはifに束縛値があれば変数として扱い、なければsyntax keywordとして扱っているからで、構文解析するときにはトップレベルから発生しているすべての束縛を記録しておく必要があるなり。
なるほど。記録しておく、というのは束縛が発生しているかいないかくらいでいいんでしょうか。

>これはよくcompile time environmentと呼ばれているなり。
compile time environmentですか。この言葉だけで全てが分かった気になりますた。
マクロの展開の話もこれですよね。つまりSICPみたく (analyze exp)とはできず (analyze exp compiletime-environment)
でないといけないと。
0412デフォルトの名無しさん05/01/22 14:15:07
>>407
>間違いなく効率が悪い。だからインタープリターでも中間コードを使ったり、コンパイラはさらに気合いを入れた最適化をするなり。
やっぱそうですか。それが嫌で、コピーするようにしたんですよね。どうせクロージャ作る時はマージするんだろうし、と思って。
でもコピーのコストが気になるのと set!に対応できないので >>401みたいなことを考えたんです。トップレベルだけがハッシュというのも納得できる
気がします。

>これは環境の複製を作って、オリジナルと複製の両方を使うということがないという意味で書いてみたんだけと、説明の書き方が悪かったかなりね。
単純にそういうことですか。理屈で分かっても実際にどういう風に操作をするのか想像できないので、ちょっと理解できなかったです。
たぶん、単にVMを作ったことないからだと思いますが。
0413デフォルトの名無しさん05/01/22 14:47:21
おれはマクロや構文のキーワードの参照したらエラーとして
弾いちゃって良いと思うけどなあ。
混乱するだけでなんも良いことないし。
それよりSchemeは名前空間関係を整備してほしい。
0414デフォルトの名無しさん05/01/22 15:09:12
>>412
ローカル変数へのset!のことを考えると、基本的に環境はコピーできない。
ただ、set!されるローカル変数は静的に解析できるので、それだけを
ヒープアロケートしたメモリに置いて間接参照し、それ以外(参照only)の
変数はコピーしてしまう、という手法はある。ディスプレイって言ったっけ?
もう記憶があやふやだが。
0415デフォルトの名無しさん05/01/22 16:32:19
>>414 のいってるやつは"orbit compiler scheme"でググればタダで論文が読めるぞ。
いろいろな最適化を幅広く解りやすくまとめてあるので
ttp://www.ccs.neu.edu/home/will/Twobit/ultimate.html
もお勧めだ。いざ進め地獄の果てまでも(笑
0416デフォルトの名無しさん05/01/22 16:42:14
>>414 >>415
そういう方法も研究されてるんですね。調べてみます。
みんな物知りですね、ほんと。
0417デフォルトの名無しさん05/01/23 00:21:04
>>415
また一人の男が門をくぐった…その門の名は lambda の門。
ガンガレおまいらw
0418デフォルトの名無しさん05/01/23 00:58:21
俺も今schemeのインタプリタ作ってるので非常に参考になります…。
0419デフォルトの名無しさん05/01/23 23:27:34
シンボルから引けるオブジェクトをスタック構造にするというのは問題あるでしょうか?
set!にも対応できるし、クロージャを作るときはトップだけをコピーすればいいし問題ないように思えるんですが
(どれをpopするか覚えとかないといけないですけど)、継続が絡むと破綻するってことはあるんでしょうか?

あと Gaucheは ifだけでなく consみたいな基本手続きも埋めこんでる様なんですが、これは時に仕様に反しないでしょうか?
(define (cons a b) (cons a b))
(cons 0 0)
は無限ループになるべきだと思うんですけど。
(define (cons a b) (cons a b))は
構文解析でcons, a, bは束縛変数だと分かるのでそれらには埋め込めないですよね
consが上書きされたら自分にアクセスされなくなるので、consに自分自身を埋めこむのはありかなと思ったんですが、
(define cons2 cons) (define cons list) とかされる可能性もあるのでやっぱ埋めこめないですね。
最適化の為に目をつぶったって感じでしょうか。

>>402 のGaucheのVMのスタック操作っていうページはかなり素晴らしいですね。
環境の扱いが検索する側にとって非効率に見えるんですけどそれでもあの速度がでるんですね。
上に書いた実装じゃ継続の実装も変わってくるだろうし、総合的に見たらどうなんですかね。

あと、実行前に構文解析するようにしても速度変わりませんでした。
lambdaとifくらいしかやってないんですけど。速くなるべきなんですかね。
0420デフォルトの名無しさん05/01/24 00:29:06
lambdaの最適化について妄想ですが、
クロージャを作るときにボディを構文解析して第一引数の使われる場所に間接参照(ポインタのポインタ)を置いておく。
第二引数以下の使われる場所にもそれぞれ別の間接参照を置いておく。間接参照は評価されると参照先のものになる。
applyされるとそれぞれの間接参照がそれぞれの引数を指すようにする。set!は間接参照をいじることで実現する。
っていうのでトップレベルの変数以外のlookupが無くなる気がするんですが、どうでしょうか。
evalの扱いが微妙になるとは思うんですが。
0421デフォルトの名無しさん05/01/24 01:32:50
>あと、実行前に構文解析するようにしても速度変わりませんでした。
>lambdaとifくらいしかやってないんですけど。速くなるべきなんですかね。
式のタイプによる分岐を実行前に事前にしておくのが肝なので、
他の構文や手続きの適用、シンボル等もやらないと。
速度は一割五分ぐらいよくなるかなあ。
■ このスレッドは過去ログ倉庫に格納されています