nekoie:log
新しい文章はnekoieにあります。
- scheme.vimとgosh_completions更新しました(Gauche-0.9対応その他)
- socketをノンブロッキングモードにしてはいけない
- Haskellのbracket
- irc用bot作成用モジュール書きました
- scheme.vim更新しました(Gauche-0.8.14対応)
- Gauche-0.8.14のインストールではまった
- Gauche用OOMパッチ
- チャーチ数エミュレート
- scheme.vim更新しました
- Gauche:$を見て思った事
- 個人的要望
- gauche-package install --clean URL ではまる
- scheme.vim更新しました(2007/10/19 08:28:02 PDT)
- implicit forceに関する妄想
- gauche.gongのkoguroさんのデモがウェブでも公開されたので、改めて見直した
- speedygoshのバグを発見してしまった(修正済/バージョン上がりました)
- gauche.night参加してきました
- 継続サーバについてあれこれ思う事をメモ
- はまった事
- speedygoshを作っていて見付けた、Gauche内部に手を入れないと越えられそうにない壁
- speedygosh
- 極簡単なwilikiのスパイダー避け
- 正規乱数の取得
- tips
- www.cgi.dispatch-tir
- CGI書き男から見たgaucheその二(というか要望)
- 困った事
- CGI書き男から見たgaucheその一
scheme.vimとgosh_completions更新しました(Gauche-0.9対応その他)
srfi-62(S式コメント)への対応がより完璧になりました。
追記: 色々と問題が発覚したので、11/27に修正版を上げ直しました。
socketをノンブロッキングモードにしてはいけない
という事を思い知った。
speedygoshが大きいデータを扱おうとすると途中で切れてしまうのはこれが原因だった。直した。
Haskellのbracket
を会社で教えてもらったので、真似してみた。
(define (bracket before-thunk after-proc main-proc) (let1 args #f (dynamic-wind (lambda () (receive r (before-thunk) (set! args r))) (lambda () (apply main-proc args)) (lambda () (apply after-proc args)))))
以下のように使う(すごい適当)。
(bracket (lambda () (values (open-input-file "in.txt") (open-output-file "out.txt") )) (lambda (in out) (close-input-port in) (close-output-port out) ) (lambda (in out) ...)) ; 長い長い処理
素のdynamic-windと比べた利点
- before-thunkとafter-procは対になる処理である事が多く、そしてmain-procは長くなる事が多いので、beforeとafterが隣合わせの方が分かりやすい
- 頻出パターンの「dynamic-wind入る前に変数用意し、before-thunkでset!し、after-thunkで終了処理」を、引数を使って短く書ける
欠点
- Haskellと違い、破壊的変更が有り得るので、main-proc内で引数(の指している先)を破壊的変更してしまうと、after-thunkに渡される引数も変化してしまう
- ただ、引数にしたいものは大体「ちゃんとした後処理をしたい何か」な事が多く、それはportとかdbmとかそんな感じの「オブジェクト」的なものが多い気がするので、破壊的変更がなされて変化しても、問題になる事は少なさそうな気はする
あとで気付いた、致命的な問題
- 継続を辿って、dynamic-wind内に再突入した際に、before-thunkは実行されるが、その引数がmain-procに渡されない(つまり、前回実行時の引数のままになる)
- これは致命的だ……これを回避するには、やっぱりset!を使うか、parameterを使うかしかなさそう。
- この問題を解決しないと、このbracketは使えなさそうだ。
- koguro(2009/01/20 04:43:47 PST): bracketの主な意図は「エラーが発生した場合でも後始末の処理を必ず実行させたい」じゃないかなと思うので、dynamic-windよりもunwind-protectの方を使うべきと思いました(Gauche限定になりますが)。そもそもmain-procを継続で抜けたり戻ったりする際にbefore-thunk, after-thunkが呼ばれてしまうのは困るケースの方が多いんじゃないでしょうか。
- nekoie2009/01/20 05:52:47 PST: 確かに、継続を辿って抜けたり戻ったりする際にbefore-thunkとafter-thunkが実行されるのは、困るケースの方が多そうです。ただ、「この後始末処理を必ずしたい」と思う時は大体、「何かを一時的に変更したり等の原因」がある訳で、その「原因の操作」と「後始末処理」はほとんどの場合で対になっているので、「可能なら並べて書きたい」と思ったのです。それと、「この後始末処理をしたい」対象というのは割と、変数に入れて指定できる(例えばportとか。変数に入りづらいものもたまにあるけど)形をしている事が多いので、「後始末をしないといけない変数はこれ」という風に、引数の形で渡せるのを綺麗だとも思ったのです。unwind-protect(と、他言語でのtry~finallyなど)では、この二点が欠けているので、Haskellのbracketを教えてもらった時に「よくできてる」と思って、真似したくなったので、これを書いてみました。
- という事で、unwind-protect版も書いてみました。
(define (bracket before-thunk after-proc main-proc) (receive args (before-thunk) (unwind-protect (apply main-proc args) (apply after-proc args))))
- という事で、unwind-protect版も書いてみました。
irc用bot作成用モジュール書きました
※しばらく当分(2009/01/05 16:22:56 PST現在)は色々と修正したりする筈なので、安定したのを使いたい場合はまだ待っててください
scheme.vim更新しました(Gauche-0.8.14対応)
Gauche-0.8.14のインストールではまった
当初、「./libgauche.so: undefined reference to `___tls_get_addr'」というエラーが出てビルドできなかったが、どうにか解決した。
- Gauche内のgcのバージョンが上がった
- gc内では、スレッドの使い方を指定する定義として、「USE_COMPILER_TLS」が定義されたり、もしくは「USE_PTHREAD_SPECIFIC」が定義されたりする。通常は、どっちになるかは自動判定
- しかし、その自動判定の条件は「gccのバージョンが3.3以降か否か」(他にも色々な条件があるが割愛)だが、実はその判定では誤判定してしまう環境もあるっぽい
- うちのマシンとか。多分、gccのバージョンではなく、glibcのバージョンが関係してるっぽい
- とりあえずスレッド関係だという事は分かったので、Gaucheのconfigureの方で--disable-threadsすればビルドできるのは確認。でもそれだと当然スレッド使えない
- 手で明示的に「USE_PTHREAD_SPECIFIC」を使うようにしたらビルドできた
同様の環境の人が他にも居るかは分からないですが、もし同様の罠に引っかかったら、Gaucheのconfigure時に、以下のようにフラグをつけるといいです(gcだけにフラグを渡す方法は分からなかった)
export CFLAGS="-O2 -DUSE_PTHREAD_SPECIFIC" ./configure ...
Gauche用OOMパッチ
- Gauche:OOMに移動しました。
チャーチ数エミュレート
私は効率性のために妥協をして、チャーチ数を使わないことにした。[2]
を見て、ちょっと思い付いたので書いてみた。
(define-method object-apply ((num <integer>) proc . args) (cond ;; マイナス値は今回はエラーにしたが、 ;; マイナス値の時も0とみなしてもいいかも ((< num 0) (error "minus number found")) ((= num 0) (apply values args)) (else ;; 本当に真面目に使うつもりなら、 ;; ここは末尾再帰に直すべき (receive r (apply proc args) (apply object-apply (- num 1) proc r)))))
(3 cdr '(0 1 2 3 4)) => (3 4)
(define (baibain x) (append x x)) (4 baibain '(1 2)) => (1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2)
これが役に立つのか、と言われると困るけど。
scheme.vim更新しました
- scheme.vim
- Gauche-0.8.13で色々と手続き等が増えたようなので、生成し直しました。
- ついでに、デフォルトのscheme.vimでは文字列補完時に、「'hoge」や「,hoge」等が、全体で一つの単語と判定されてしまって補完されなかったのを修正しました(単にiskeywordから「'」「`」「,」を抜いただけ。「@」は普通にシンボル構成文字なのでiskeywordから抜けないので困った)。
Gauche:$を見て思った事
- これいいなあ。
- でも、Lisper的には、括弧を減らす事は構造が分かりにくくなるデメリットもあるような気がするので(とは言え、Arcはletの括弧とか減らす方向だったけど……)、Haskellから何か貰ってくるなら、「$」ではなく、「>>=」とかの方がいい気もしました。
fn1 (fn2 (fn3 (fn4 x)))
を(fn4 x) >>= fn3 >>= fn2 >>= fn1
の意味で、(>>= (fn4 x) fn3 ; とか、(cut fn3 <>)とか fn2 fn1)
rubyとかのメソッドチェーンっぽく。 - これ結構簡単に書けそうなので、あとで書いてみる。
書いてみた。
-
(define-syntax >>= (syntax-rules () [(_ expr next . rest) (>>= (receive tmp expr (apply next tmp)) . rest)] [(_ expr) expr]))
(>>= (+ 1 2) (pa$ + 4 8) (pa$ + #?=16) (pa$ + #?=32) (pa$ values 64) (cut + <> <>)) #?=16 #?- 16 #?=32 #?- 32 127
それっぽく動いてるっぽい。 - しかしこれ、既に別名で存在しそうな気がしてきた。
- あと、cutやpa$だらけになるのがちょっと駄目だと思った。その点でGauche:$の方が優れてる。
- 齊藤(2008/10/19 00:02:55 PDT): compose の結果を適用するのと似た感じですね。
((compose (cut + <> <>) (pa$ values 64) (pa$ + #?=32) (pa$ + #?=16) (pa$ + 4 8)) (+ 1 2))
個人的要望
- c-wrapperをGauche組み込みにしてほしい
- LALRパーザのライセンスがGPLだから無理?
gauche-package install --clean URL ではまる
- とある環境(SUSE LINUX Enterprise Server 9.3 / wget-1.9.1)でタイトル通りのコマンドを使ってインストールしようとしたら、gauche.package.fetch内のURL取得部分で、SuSEのwgetが「-P .」コマンドを勝手に「-P _」に変換し、結果として、tgzが「_/WiLiKi-0.5.3.tgz」みたいに、「_」というディレクトリを新たに作成してその中に保存される事になり、それ以降の処理が失敗してエラーになった。
- 普通にwget URL; gauche-package install --clean TGZみたいに二段にすれば問題ないが、半分ぐらいスクリプト状態にしてやってたので、気付くのが遅れた。
- 多分セキュリティ問題回避のパッチか何かがSuSEによってあてられてこの状態になってるんだと思うけど、ちょっとひどくないかSuSE。
- 多分、SuSE最新版のwgetでは直ってるような気がするので、運用で回避しよう。
- SUSE Linux Enterprise Server 10 / wget-1.10.2 で、普通に動作してるのを確認。やれやれ……。
- 多分、SuSE最新版のwgetでは直ってるような気がするので、運用で回避しよう。
scheme.vim更新しました(2007/10/19 08:28:02 PDT)
- scheme.vim
- Gauche-0.8.11のモジュール類に対応しました。
- 生成手順を書きました。
- 生成途中の束縛一覧とかはemacsな人も再利用できそうな気がします。
- と思ったら、emacs用のはずっと高性能なのがあるようだ。
- http://d.hatena.ne.jp/hayamiz/20071022/1193019334
- emacsいいなあ。またemacsに再挑戦してみようか……仕事で今やってる案件が一段落したら。
implicit forceに関する妄想
http://d.hatena.ne.jp/ranekov/20070923#c1190540213 でもらったコメントを見て考えてみた。
- 手続きの適用時に、(srfi-10の読み込み時コンストラクタみたいな感じで定義する)フックを定義できる機能を実装すれば、楽に実現可能?
- 普段はimplicit forceはいらないので、(with-implicit-force ...)の内部でだけimplicitになるようにすれば、フックによる速度低下も気にする必要はあまり無い?
- しかし、Gauche内の、Cで書かれた手続きから、直接、Cで書かれた他の手続き(例えばcdrとか)を呼び出している場合は、結局、cdrを呼び出す前に明示的にフックを呼ぶコードを入れてまわらないと駄目そうなので、実装が大変なのは変わらなさそうだ……。あと、cdrとか頻出しそうなところにフックのチェックを入れるのは、それだけでパフォーマンス落ちそう。駄目っぽい。
gauche.gongのkoguroさんのデモがウェブでも公開されたので、改めて見直した
- 当日はあまりのキャッチーさ(リリカルLispとか黒板とか)に惑わされてたけど、改めて見ると、これ、物凄いGaucheのキラーアプリになるように思える。多分、pygame並か、それ以上に。
- これは今すぐ試さなくてはならない、と思ったものの、どこから貰ってくるのか全然分からない!うわーん。
- koguroさんにLingrで聞いたら、今ドキュメント整備中で近日中公開だそうなので、楽しみに待っておく事に。
- Gauche:Packagesでパッケージが公開されたので、やってみた。これは凄い!いじっててニヤニヤしてしまいます。
speedygoshのバグを発見してしまった(修正済/バージョン上がりました)
- プレゼンをやって、ドッグフードを自分で食わなくてはと思い、本格的に自分のところのscmailをspeedygosh化した(これまではテスト用途にしかspeedygosh化していなかった)。
- そうしたら、実は、scmailをspeedygosh化したら、メールが全スルー(メールは普通に届くが、スパムも除去対象メールも全部メールボックスに入ってしまう)状態になってる事が判明した。
- 原因は、scmailが設定ファイルを読み込む際にload-from-portを使っているが、そのロードしている内容でadd-filter-rule!が使えなくなっているのが原因。
- speedygoshでは、読み込むスクリプトはuserモジュールではなく、特定の無名モジュールにロードするようにしていたが、この処理のせいのようだ。
- 元々は、複数のスクリプトが一つのプロセスに同居可能なように、このような仕様にしたような雰囲気だが、実際問題として、同じ名前のモジュール空間であっても、プロセスによって、微妙にバージョンの違う内容のモジュールが入る可能性がある(のをperl仕事で経験していた)ので、結局、1スクリプト1プロセス完全に割り当てる仕様にした。
- しかしそれなら、無名モジュールにロードしなくてもよいので、userモジュールにロードするように直せば解決しそうな気がする。
- しかし、src/load.cの該当部分を見ると、モジュールが無指定の時はScm_VM()->moduleでモジュールを取得するようになっているので、無名モジュールでも問題なく動きそうな気も……もう少しちゃんと調べよう。
- 分かった。スクリプト本体のmainを実行する前にwith-moduleし忘れてたのが原因だった。これで直る。
- と思ったら、無名モジュールではwith-moduleできないっぽい。どうしよう。
- いや、できないように見えるのは、with-moduleがspecial formで、モジュール指定部分がシンボルとして解釈されるのが原因だった。以下のようにしたら出来た。
;; (current-module)は、実際に行いたい操作の代わり gosh> (eval `(with-module ,(make-module #f) (current-module)) (interaction-environment)) #<module #>
- そしてこれは、結局、以下をやってるのと同じという事に気付いた。
gosh> (eval `(current-module) (make-module #f)) #<module #>
- 急いで直します。
- 直した。scmail動くようになった。バージョンアップした。
- お手数かけてすみませんが、古いspeedygoshをインストールしてしまった方は、最新版にアップデートお願いします。
gauche.night参加してきました
すごかった。 皆様どうもお疲れ様でした。
とりあえず印象に残った事を。
- koguroさんのデモが最高でした。黒板万歳!
- Shiroさんと黒田さんの対決がすごかった。あと黒田さんに、ILC2007の資料を見せてもらいました。どうもありがとうございます。興味を惹かれる内容が色々ありました。
- Theoria?さんがカッコ良かった。記念品おめでとうございます。
- 終わった後に、残った方々で話とかゴルフとかジャグリングとかエディタ比べとか色々。終わった後も濃かったです。気付いたら時間がなくなってたのでちゃんと挨拶できずに帰ってしまいましたが、あの後どうなったのか気になります。
とにかく濃かったです。 参加して良かった。
継続サーバについてあれこれ思う事をメモ
gemma:継続サーバを見てみました。
なるほど、セッションが明示的に生成される毎に、継続プロセスも明示的に生成する手があったんですね。
昔、自分もCGI等で使える、lightweightな継続保持システムが欲しくなって、ちょっと試しに作ろうとしてみたのですが、「継続(やlambda)のシリアライズ」と「入出力の抽象化」の二つの部分が上手くいかず諦めました。 (Kahuaは、常駐動作するので継続をシリアライズする必要は無いし、入出力はSXMLで上手く抽象化されてます。)
しかし、よく考えてみると、継続保持が必要な状況というのは大抵、管理画面CGIやら登録制ウェブサービスやらネットワークゲームやらの、「事前にログイン(またはセッションに相当する何か)を行ってから本処理を行う」用途が大部分のように思えます、自分には。 (と言うか、ログインとか無しに継続保持動作をサーバにやらせられる状態は、ウェブクローラからのアクセス等によってDoSっぽい事になったりしそうで、ちょっとまずそうです。) なら、それに特化してもいいかもしれない、と気付きました。
具体的には、ログイン(か新規セッション作成)される毎に、gemma:継続サーバのように、ログインidやセッションidをkeyにした継続プロセスを生成する(つまり、1クライアントにつき1プロセスを完全に割り当てる形になる)、セッション部分の管理までも含めた、セッション付き継続保持システム用モジュール。
これなら、ログイン(セッション)単位での管理になるので、一定時間アクセスがなければタイムアウトして継続を破棄するような実装でも(データ保存さえちゃんとできているなら)文句は言われないし、継続の破棄タイミング=タイムアウト(またはログアウト)という事で分かりやすいし、実装も割と簡単そうです。 (多分、自分の書いたspeedygoshとsocket.server(今中途半端な状態。また今度ちゃんと作り直す予定)の中間ぐらいになりそうに思えます。)
その内作りたいです。 (多分、socket.serverの改良版の次ぐらいに作りそう。そっちを先に作り直した方が作るの楽そうだから。)
ですが、他の人も作りたかったら、どうぞ作ってください。 (その時には、よかったら自分にも使わせてほしいです。)
はまった事
- with-error-handlerは、エラーが起こったクロージャ内でのcurrent-*-portを保持したままエラーハンドラを実行する(エラーハンドラ内のcurrent-*-portはクロージャ内の物になる)。
- guardは、エラーハンドラが実行される時も、静的スコープ通りのcurrent-*-portになる。
両方とも同じだと思ってたら、ひどい目にあった。
これはちょっと、リファレンスに書いといてほしい……。
- び(2007/03/27 15:04:01 PDT): with-error-handlerに、:rewind-before #t というのをつけると、guardと同じ挙動になるみたいです。EXPERIMENTAL ってソースコードに書いてあるくらいだから使っていいものかわかりませんが...
gosh> (with-error-handler (lambda (e) (display "hoge")) (lambda () (with-output-to-string (lambda () (display "moke") (error "Boooon"))))) #<undef> gosh> (with-error-handler (lambda (e) (display "hoge")) (lambda () (with-output-to-string (lambda () (display "moke") (error "Boooon")))) :rewind-before #t) hoge#<undef> gosh>
- Shiro(2007/03/27 17:54:02 PDT): 使わないで下さい。:rewind-beforeはguardの動作を
srfi-34互換にするために入れたハックです。
確かにguardのこの動作はリファレンスマニュアルに書いてませんね (with-error-handlerの方は書いてある)。もともと
srfi-34のこの仕様を見落としてたもんで。書き足しときます。
guardが実装されたために、with-error-handlerの必要性はいまやほとんど 無くなりました。エラー時にデバッガに入る時に必要かもしれないと思っているのですが (ハンドラでdynamic-environmentを巻き戻さないのはそのため)、 より低レベルのwith-exception-handlerで効率良く書けるようなら、将来的には with-error-handlerをフェードアウトさせるかもしれません。なるべくguardを 使って下さい。 - Shiro(2007/03/27 20:04:46 PDT): 注意書きを足そうと思ったらCVS HEADには既に足してあった…
- nekoie(2007/03/28 05:32:06 PDT): なるほどです。了解しました。
speedygoshを作っていて見付けた、Gauche内部に手を入れないと越えられそうにない壁
とりあえず三つ。
- sys-putenvがメモリリークする(とりあえず(gc)しても回収されないところまでは確認)
- sys-system実行時のstdout/stderrの変更(本質的には、(standard-*-port)の変更のサポート)
- (exit n)が実行された時に、nの値に応じた処理を終了直前に行いたい(atexitに似ているが、その時に終了値も参照したい。単なるatexit的挙動だけならdynamic-windで可能だと思う)
speedygoshとしては「サーバプロセス側もCで書いて、libgauche経由でスクリプトを評価させる」方法で、この三つは解決するし、そうすればGauche以外のスクリプト言語に移植しやすくなるというおまけもつく。
しかし、それに今手を出すのはちょっと微妙にハードだ。
一番目(と二番目の一部)に関しては、Gauche内部で**environ的なテーブルを保持して、普段のsys-getenv/putenvではそのテーブルだけを操作するようにし、sys-systemやexec類の実行前にのみ(もしテーブルが変更されているなら)putenv等で再設定するようにすれば解決すると思う。 環境変数をlistとして扱えるようになるというメリットもある。 おそらく他のスクリプト言語もそうしてると思う、多分。 これの問題点は、putenvをサポートしていない環境をどうするかという部分ぐらいだと思う。
ソースをパッと見た感じだけだと、これはそんなに難しくなさそうだから、どうしても必要になりそうなら、自分でパッチを書いてしまおう。
- び(2007/03/27 15:32:08 PDT): 原典に当たってないのでアレですが、autoconfのinfoに次のような記述がありました。
`putenv' POSIX specifies that `putenv' puts the given string directly in `environ', but some systems make a copy of it instead (eg. glibc 2.0, or BSD). And when a copy is made, `unsetenv' might not free it, causing a memory leak (eg. FreeBSD 4).
なので、POSIX的に見るとGaucheの措置は間違ってないと思いますが、現在のメジャープラットフォーム(?)でのペナルティが大きいですね... - Shiro(2007/03/27 17:54:02 PDT): putenvに関してはリークは折込み済です (ext/auxsys/auxsyslib.stub)。もともと「sys-putenvなんてその後execする時しか 使わんだろ」程度に考えてたんで。確かに他で書かれたsys-getenvしてるスクリプトを 自プロセス内で実行したい場合などはputenvを何度も呼ぶ必要がありますね。 Gauche内で環境のコピーを持つ方法を検討してみます…ってそれだと、C extension内で getenv()を呼んでいる場合にまずいことが起きますね。「リーク覚悟でputenv()を 呼ぶか、C extension無視でラッパーを使うか」、というのを選べるようにしとくしか ないかも。…ってだめだ。dlopenだとか、環境変数を参照する外部ライブラリってのは いくらでも有り得るから、やっぱりシステムのenvironをいじるしかない。
- Shiro(2007/03/28 02:34:02 PDT): 結局、setenv(3)がある場合はそっちを使うことに。
- nekoie(2007/03/28 05:32:06 PDT): あ、なるほどです……C extensionもある事を忘れてました。お二方、どうもありがとうございました。
二番目の問題は、ちゃんとは見てはいないがおそらく、変更処理自体は可能だと思う。 ここで問題になりそうなのは、例えば、(standard-output-port)がGaucheで書かれたvportに変更された後にsys-systemやexec類が実行された場合。
sys-systemの時は、適当にpipeや一時ファイルをstdout/stderrに割り当ててから実行し、完了してから、この場合は(standard-output-port)よりも(current-output-port)に流し込んだ方が親切なんだろうか……これはちょっと微妙そうな問題だ。 正直、その辺を気にするような人なら素直にgauche.processを使うと思うから、どっちでもokそうな気はする。 exec類だった時は、(standard-output-port)が普通の*FILEだったらそれを採用、vportとかだったら/dev/nullにする、とかにすれば、一応いけそうな気はする。 それか、単にvportを設定しようとした時はエラーにするか。
ここはちょっと仕様が微妙な事になりそうだ。 諦めた方がいい?
- Shiro(2007/03/27 17:54:02 PDT): sys-systemは使い捨てスクリプトで簡単に外部プロセスを
呼ぶ手段、くらいに考えて下さい。余分な機能を追加する予定はありません。
stdioの入れ替えなど、それ以上のことをしたければgauche.processを使って下さい。
ただ、デーモン化する際などにstdioを入れ替えたいって需要はあるので、dupに 類する何かを追加する予定はあります。 - nekoie(2007/03/28 05:32:06 PDT): 了解しました。そのようにします。
最後の三番目の仕様は、speedygosh以外には需要なさそう。
- Shiro(2007/03/27 17:54:02 PDT): exitにフックをかけられるようにする予定はあります。
interactive REPLでスクリプトのデバッグをしている時とかに、exitされると嫌だし。
実はこれをexception handlingの枠組でやろうかと思っていたんですが
(exitは<application-exit>という例外を投げる。そのデフォルトハンドラが
実際のexit処理を行う)、それを実装して仕事で使ってみたらいまいち使い勝手が
悪くてrevertしちゃいました。
しかし、とりあえず対応するならexitを再定義するだけじゃだめですかね? - nekoie(2007/03/28 05:32:06 PDT): なるほどです。ええっと、読み込んで実行したいスクリプトが単一モジュール空間だけで完了してる場合なら再定義して終わりなのですが、そこからライブラリ的に他のモジュール空間を参照していて、そのモジュール空間内でexitを参照されてる場合に対応が漏れてしまうので、やるならフックをつけられるようにするか、scheme空間を呼び出す前のC空間で処理を行うかのどっちかにしないと本質的には駄目かなあ、と思ったのです。とは言え、そんなexitの使われ方はscheme的に行儀が悪いという事で、exitを再定義するだけの方針でやっちゃってもいい気もしています。
そこまで考えると、やっぱり一番真っ当なのは「サーバプロセスもCで書く」という事になる。
自分の見積りでは、自分のCの実力でサーバプロセスをCで書いてとりあえず動く状態にまで持っていくのに、40時間の空き時間は欲しい感じだ。 普段の仕事と他の遊びもあるので、日数にして大体20日~一ヶ月ぐらい? これは微妙だ。 どうしよう。
- koguro(2007/03/27 18:47:16 PDT): 完全なDirty Hackですが c-wrapper でgauche.hをインクルードすると自分のgoshの内部を操作することができ、Cと同等なことがGaucheレベルでもできます(できるはず?)。とはいえ、手間もCで書くのと同じくらい面倒なので素直にサーバプロセスもCで書いた方がよいかも。
- nekoie(2007/03/28 05:32:06 PDT): おお、これは凄い面白そう。なんか色々と応用が利きそうなやり方で、これはこれで楽しそうです。これでちょっと遊んでみます。ありがとうございます。
speedygosh
speedycgiのgosh版を開発中。未実装部分がまだ多い。
極簡単なwilikiのスパイダー避け
Shiroさんの2006/09/25の日記で、履歴差分をスパイダロボットがもっていって負荷が高くなる、という問題が出ていたので、とりあえず通常のスパイダロボットならformボタンは辿らない、という前提で、編集と編集履歴のリンクをボタン化してみた。
- http://d.tir.jp/pw
- http://d.tir.jp/wiliki.diff (なんか余計な処理もまざってるので要確認)
ただ、この方法だと、編集履歴一覧ページは元のままなので、既にさんざんスパイダロボットにもってかれた後だと意味なさそうだけど。
編集履歴一覧ページの一覧リンクもボタン化すればokそうだけど、見栄えが悪くなりそうなのでやってない。
真面目に実装するなら、User-Agentを見て判断するしかなさそうだが、それも微妙な問題が色々出てきそうだ。
正規乱数の取得
- 昔から、結果が正規分布になるような乱数が、様々な用途で欲しくてたまらなかったが、面倒なので放置していた。
- この前、http://ja.wikipedia.org/wiki/%E4%B9%B1%E6%95%B0 を発見したので、軽く実装してみた。
;;; - http://ja.wikipedia.org/wiki/%E4%B9%B1%E6%95%B0 ;;; -- ボックス=ミューラー法による実装 (use math.mt-random) (use math.const) (define *mt* #f) (define (make-mt!) (unless *mt* (receive (epoch micro) (sys-gettimeofday) (set! *mt* (make <mersenne-twister> :seed (+ epoch micro (sys-getpid))))))) (define (normal-distribution-random . keywords) (make-mt!) ;; note: この関数は二つの正規乱数を多値で返す。 ;; (一つしか必要ない場合は片方は適当に捨てれば良い) ;; キーワード引数は以下の通り。 (let-keywords* keywords ( (sigma 1) ; 分散度を指定。 ;; sigmaが大きいと平均的に分散し、 ;; sigmaが0に近いとmu近辺しか出なくなる。 (sigma-plus #f) ; +方向と-方向で、sigmaの値を (sigma-minus #f) ; 変更したい場合に個別に指定する。 ;; 尚、sigma-plusかsigma-minusにマイナスの値を ;; 指定する事で、半分に折り返した正規分布を ;; 作る事も可能。 (mu 0) ; 一番出やすい期待値(平均の中央)を指定。 (clamp-min :min #f) ; 結果をclampする場合に指定 (clamp-max :max #f) ; 結果をclampする場合に指定 ) ;; 「(0, 1]」の乱数を二つ用意する (define (get-r) (- 1 (mt-random-real0 *mt*))) (let ((alpha (get-r)) (beta (get-r))) ;; 以下の計算を行う事で、二つの相関の無い正規乱数が得られる。 ;; ((-2 * log(α))^0.5) * sin(2 * PI * β) ;; ((-2 * log(α))^0.5) * cos(2 * PI * β) ;; 左辺(result-alpha-part)と、sin/cosの中味(result-beta-part)を先に求める (let ((result-alpha-part (expt (* -2 (log alpha)) 0.5)) (result-beta-part (* 2 pi beta))) ;; 二つの正規乱数を得る。 (let ((result1 (* result-alpha-part (sin result-beta-part))) (result2 (* result-alpha-part (cos result-beta-part)))) ;; 上記二つの正規乱数を加工して返す (define (post-process r) (clamp (+ mu (* r (or (if (positive? r) sigma-plus sigma-minus) sigma))) clamp-min clamp-max)) (values (post-process result1) (post-process result2)))))))
これで、それっぽい様々なデータを捏造する事が出来る。
間違いや改良点とかあれば、教えてください。
使いたいと思った方は、適当に持っていってください。
- scheme.vimを久し振りに更新した(と言うか、ほぼ作り直した)。
- 0.8.6の関数類一通りと、Gaucheの正規表現に対応できた。
tips
- www.cgiでstack traceを出力するには
- cgi-mainの:on-errorに、次のようなprocを指定する。
(define (cgi-on-error/stack-trace e) `(,(cgi-header) ,(html-doctype) ,(html:html (html:head (html:title "Error")) (html:body (html:h1 "Error") (html:pre (html-escape-string (call-with-output-string (cut with-error-to-port <> (cut report-error e)))))))))
- cgi-mainの:on-errorに、次のようなprocを指定する。
- with-output-to-string等の、一時的に(current-output-port)を変更する関数は、便利だが、内部でエラーが起こった時に、ものすごくややっこしくなる可能性を秘めている(エラーハンドラ内では、with-output-to-string等で設定された(current-output-port)等が、まだ有効な為)。
- 普段は、call-with-output-string等を利用して、絶対にエラーは起きないと確信できるような時だけ、with-output-to-stringを使うようにしたい。
- また、with-error-handlerのエラー処理を行う部分で何か出力するようなら、必ず、with-error-handlerの前で(current-output-port)を予め記憶しておき、そっちに出力するように心掛けたい。
- Schemeでの命名規則は、副作用のある関数名の末尾に!を付ける。
- しかし、副作用がある関数でも、portの入出力を行うだけの関数には、末尾には!は付かない/付けない、ようだ。
www.cgi.dispatch-tir
- Kahuaのdefine-entryを見て、「おお、マクロって素晴らしい」と感動して、KahuaじゃないCGIでも使いたいと思い、汎用化したモジュールを書いてみた。
- 能書き。
- 自分でディスパッチテーブルを書く必要がなくなる(ディスパッチする為のCGIパラメータはdefine-cgi-entry時に指定)。
- CGIスクリプト自身へのリンクやフォームを書く時に、CGIパラメータを埋め込まずに、define-cgi-entryで束縛したシンボルを指定するだけで済む。
- 微妙に重い。
- www.cgi.dispatch-tirを使ったサンプルソースと、実際に動いている例。
- 更に細かい色々は、自分のページにあります。
某所の何かを見てTypeKeyを使いたくなったので、突発的にTypeKey認証モジュールを作りました(と言うか、移植しました、pythonのTypeKeyモジュールを)。
- ソース
- 動作サンプル
- 動作サンプルのソース
- ライセンスを元のpython版モジュールにあわせて、GPL2にしました(2005/10/20 07:52:07 PDT)
- フォームhtml(のtext.tree)を生成する関数を追加しました。Gauche本体と同じライセンスにしました(仮)(2005/10/19 12:27:07 PDT)
- いちいち細かい引数を渡さなくても、(verify <typekey>)で認証チェックできるように、methodを追加してみました。(2005/09/28 18:23:24 PDT)
- 作ったはいいものの、ライセンスとかどうしよう。
- 元のpython版モジュールのアルゴリズム貰ってきたから、原作に準ずるなら、
GPL2になるけど、できれば、Gauche本体と同じく、BSD風ライセンスにしたい。
コレって、原作者の人に英語でメール送って許可もらわないといけないんだろうなあ……。
それとも、GPLの精神からすると、そういうのは駄目なんだろうか。
- アルゴリズムは著作権で保護されないので大丈夫だと思います。(←間違ってたら訂正してください)
- メールを出してみたものの、怪しげな英語が不味かったのか、返事が来ない。インターフェースはpythonのモジュールをベースにしたとは言え、コード的には全部自分が書いたものなので、大丈夫という事にして、BSD風ライセンスにする、という事にしたいと思います。
- マズイでしょ。「移植」なんでしょ? それだったらライセンスも「移植」されたと見なすのが自然ですし、法的にもそうなるんじゃない? でなきゃ、Windowsのソース見てからLinuxのコード書けちゃうじゃん。もちろん、作者から「変えていいよ」っていわれたんなら別だけど。いや、ちょっと違うか。Windowsのソース見て、それをSchemeで書き直してもOKってことにはならないでしょ? もちろん、WindowsがSchemeで書かれててもダメだけど。
- なるほどです……。ライセンスはGPL2にします。
- アルゴリズムは著作権で保護されないので大丈夫だと思います。(←間違ってたら訂正してください)
- 元のpython版モジュールのアルゴリズム貰ってきたから、原作に準ずるなら、
GPL2になるけど、できれば、Gauche本体と同じく、BSD風ライセンスにしたい。
コレって、原作者の人に英語でメール送って許可もらわないといけないんだろうなあ……。
それとも、GPLの精神からすると、そういうのは駄目なんだろうか。
- 使いたい人は適当に持っていって、(use typekey)して下さい。ソース本体にも、日本語ですがメモとか書いてます。
- ちなみに、コード自体はかなり微妙なものの、動作自体はちゃんと動いてます。
- tcpcgiを作って、動かしています。
- ↑のリンクのe.tir.jpサーバはtcpcgi+tcpserver+squidで動作しています。
CGI書き男から見たgaucheその二(というか要望)
www.cgiモジュールに、ces判別用のinput hiddenタグの文字列から、送信されたフォーム内容全体のcesを判定する機能が欲しいです。
- 結局のところ、蔓延っているosの日本語エンコーディングがsjisの環境が大多数なので、htmlを吐き出す時は、sjisで吐き出したい。
- しかし、サーバ側のosの日本語エンコーディングはeucなので、スクリプトはsjisで書きたくない。
- 色々あるHTTPクライアントの中には、htmlの日本語エンコーディングを気にせずに、osのネイティブな日本語エンコーディングでフォーム内容を送信するものもある。
- だから、吐き出すhtmlのエンコーディング種別に関わらず、常に、クライアントから送られてきたフォーム内容のcesを自動判別した方が無難。
- しかし、送られてきたフォーム内容が、「耳」とか「ココ」とかだけで、自動判別に失敗するパターンがある。
- そこで、ユーザが入力できる欄とは別に、input hiddenで、常に自動判別に失敗しない日本語文字列をコッソリ入れておいて、ソコを使って、送信されたフォーム内容全体のcesを判定する、という手法をとりたい。
- しかしコレを行うには、www.cgiの改造が必要…‥。
- Shiro(2003/12/29 15:58:19 PST): なるほど。ただ、この処理は日本語の事情に特有なので、
やるならwww.cgiとはレイヤーを分けたいです。とりあえず、アプリケーション
レベルでそういう仕掛けが必要な時に、フックをかけられる仕組みを作っておくのが
良いように思います。こんな感じかな:
- 必要なhiddenフィールドはアプリケーション側で用意する
- フォーム引数をパーズするルーチンで、特定のフィールドをチェックした後で
他のフィールドのcesを指定できるようなフックを追加する。
- うーん、なるほどです。確かに日本語特有ですね…‥。とりあえず考えてみます。 -- nekoie (2003/12/29 23:56:24 PST)
- Shiro(2003/12/30 04:16:26 PST): あと、サーバ側から送るフォームのエンコーディング ですが、これはwww.cgiのcgi-output-character-encodingを設定してやれば、 「スクリプトはeucjpだけどcgiが返すのはsjisで」というようなことは可能です。 参考迄。
困った事
- 何処かに、どうにかすると、オブジェクト自体を実行可能
(要するに、「(#/hoge/ "hogehoge")」のような事を実現)にできる、
ような内容を何処かで見かけたが、見失った‥‥。
一体何処で見かけたんだろう。
gauche.regexpを見てもそれらしきものは無いし。
- み、見つけた‥‥。Gauche:クロージャの中身。コレを使えば、plambdaも簡単に作れる?
- plambdaは自分の腕前を超えてるので、今は諦め…‥。
- み、見つけた‥‥。Gauche:クロージャの中身。コレを使えば、plambdaも簡単に作れる?
- プロセスを越えてlambdaとそのクロージャ(この表現で合ってるか怪しげ)を
共有したいので、何とかして、変数にバインドされている中味のlambdaを
ファイルやdbに書き出したり、それを読み込んだりしたい。
でも、やり方が分からない。
- Shiro (2003/08/19 13:34:40 PDT): →Gauche:プロセスを越えたlambdaの移送
- どうもです。現在の自分の力量では足りない気がかなりしますが、plambdaを作ってみます。
- 駄目だった…‥(自分の腕前が)。
- どうもです。現在の自分の力量では足りない気がかなりしますが、plambdaを作ってみます。
- Shiro (2003/08/19 13:34:40 PDT): →Gauche:プロセスを越えたlambdaの移送
CGI書き男から見たgaucheその一
ええっと、一般的に、ソレっぽいウェブアプリを書く場合、 以下の二点のサポートが重要な気がします。
- 高速起動
- メジャーなデータストレージエンジンのサポート
前者は、要するに、素のスクリプトを毎回起動しているのでは、遅くて負荷もかかって、
とてもやっていられない、という事で、 大体三パターンのアプローチが取られている気がします(後述)。 gaucheでは一応httpd.scmがありますけど、自分としてはプロセスを永続化できる SpeedyCGI のような仕組があると自分がとても嬉しい気がします。
後者は、gaucheではndbm, gdbmがサポートされてますけど、最近のソレっぽい事を 始めようとか思う人が既に使ってたりして慣れてるのはやっぱり、 postgresとmysqlでしょう。
高速起動の三パターン
- httpdプロセスに、言語インタープリタを組み込む
- php, mod_perl, mod_ruby等、mod_言語名っぽいモノが該当。
- この手法では、httpdの一プロセスが使用するメモリ量が肥大化する傾向があり、 また、子プロセスがメモリリークをしていると、全体に被害がかかるという 問題もある、ような気がする。
- php, mod_perl, mod_ruby等、mod_言語名っぽいモノが該当。
- その言語自体でhttpdを実装し、そのappletとして色々なモノを起動。
- pythonのzopeが該当?httpd.scmもこの方向。
- これも、appletがメモリリーク等すると、全体に被害がかかる?
- ちょっとこっち方面は未調査気味。
- localhostの別portに動的コンテンツ専用のhttpdを立て、 apacheのmod_proxyを使ってアクセスさせて、通常ファイルはapacheに扱わせる、 というような事も可能。
- pythonのzopeが該当?httpd.scmもこの方向。
- スクリプト自体が単体プロセスとして常駐し、起動したらソケットファイル等を置いておき、
次回に該当スクリプトが実行された時に、常駐しているものが既に起動しているなら、
それを利用するような仕組(説明しづらい‥‥)
- FastCGI, perlのSpeedyCGI等が該当。
- これらは、一定時間アクセスが無かったら等の条件を使って、自分自身を 自動的に自動終了できる。ので、メモリリーク等が起き難い(少なくとも、 障害が起きても、httpd自体に影響を与える事は少ない)。
- また、SpeedyCGIは名前に反して、CGI以外の、例えば五秒に一回スクリプトを 実行させるような用途にも利用できる。 自分としてはコレのgauche用が欲しいです‥‥(自分はSpeedyCGIのソースみて挫折)。
- FastCGI, perlのSpeedyCGI等が該当。
- Shiro (2003/08/19 13:52:04 PDT): (CGI用途に限らず)サーバとして動作させるのは さほど難しくないと思いますが、ライブラリサポートがあった方が良いですね。 考えてみます。
- 通りすがりのものですが、例えばmod_jkやmod_webappなどととお話しできるようなサーバを 作ってあげるといろいろ便利かなぁなどと思っています。いずれにしても Servletのようなフレームワークが整備されていた方が楽ですよね。 自分ではなかなか手が回りませんが...
- ねるWiki:ねる 2003/08/20 22:51:15 PDT: つねづね、高機能なGaucheで書かれたアプリケーションサーバが欲しいと考えていましたのでお話に参加させてください。mod_webapp 互換とかは良いかもしれませんね。httpdくみこみのredirectorとサーバプロセスの対でサービスを提供するのが高機能を追及するには良いきがします(continuation passingで書くとか楽そう)。rubyで最近WebObjectsのまねしたframeworkがでてましたよね。ああいうの欲しいなぁ.... (おまえが書けとかいわれそう)
- nekoie 2003/12/29 11:27:45 PST 自分もhttpdに限らない、サーバ動作する奴が欲しいですが、自分で作る腕前は…‥。
mysqlサポート
とりあえず、自分でも、必要最低限使えるレベルの、 mysqlコマンドを叩くモジュール を作ってみました。 そして、その ちょっとした動作テスト 。
- dbd.mysql使えるようになったので、消しました。
- Shiro (2003/08/19 13:52:04 PDT): 目指す方向はDBI/DBDアーキテクチャですが、 当面はそんな感じでお願いします。postgresについては Gauche:Packagesに拡張ライブラリがあります。